手工配置springboot + spring security + thymeleaf + thymeleaf-extras-springsecurity

原创
2018/03/09 16:08
阅读数 1.4W
thymeleaf-extras-springsecurity是thymeleaf模板框架与spring security框架的扩展包,主要实现在thymeleaf的模板页面中可以很方便的调用security框架返回的用户验证和权限信息。因为spring boot并没有对该扩展实现自动配置,因此在使用该扩展时需要自己实现thymeleaf的配置与该扩展的集成。

 

spring boot中thymeleaf的自动配置

Spring Boot要使用thymeleaf很方便,只需要引入spring-boot-starter-thymeleaf的依赖即可实现自动配置。

<!-- pom.xml -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

默认使用的是thymeleaf2,如果想使用thymeleaf3,可以在pom文件中配置thymeleaf和dialect的版本

<!-- pom.xml -->
<properties>
    <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
    <thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>
</properties>

我这里使用的就是thymeleaf3,因为有我喜欢的html模板,且全面支持html5

spring boot的自动配置已为您按默认配置好了ViewResolvers,您只需将html文件放到src/main/resources/templates下,就可以在controller通过视图名称使用对应的html文件了

如果我们不使用thymeleaf-extras-springsecurity扩展的话,我们只需要在application.properties中配置一下thymeleaf使用html模板即可

# 使用html模板
spring.thymeleaf.mode=HTML
# 编译缓存,开发环境写false,更新页面可马上看到效果;生产环境用true,速度快一些,但更新页面后需重启应用才能看到效果
spring.thymeleaf.cache=false

关于使用thymeleaf-extras-springsecurity扩展,需要单独写配置类配置,那是本文重点,后边会有描述。

 

Spring security集成

首先引入spring security的依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

要使用spring security,还需要手工做一下配置,这里是一个简单的基本配置

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    //将密码写在application.properties文件里边,并在配置类中读取
    @Value("${app.admin.password}")
    private String adminPassword;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置一些静态地址的访问路径不通过权限验证,以及登入及登出的配置
        http
            .authorizeRequests()
                .antMatchers("/themes/**", "/assets/**", "/components/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        //简单的配置了使用内存的验证方式,固定一个用户名和密码
        auth
            .inMemoryAuthentication()
                .withUser("admin").password(adminPassword).roles("ADMIN");
    }

}

 

控制器及视图实现

然后再写一个负责登入登出的controller

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MyController {
    
    //登入后的首页
    @RequestMapping("/")
    public ModelAndView homepage(HttpServletRequest request){
        return new ModelAndView("index");
    }

    //登入地址,当访问任何需验证授权的资源时发现未登入,通过之前的security配置会自动跳转到该地址
    @RequestMapping("/login")
    public ModelAndView login(){
        return new ModelAndView("login");
    }
    
    /**
     * 登出处理
     */
    @RequestMapping("/logout")
    public ModelAndView logoutView(HttpServletRequest request, HttpServletResponse response){
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null){    
            new SecurityContextLogoutHandler().logout(request, response, auth);
        }
        
        return new ModelAndView("redirect:/");
    }
}

然后是一个登入窗口页面,使用thymeleaf3的html模板

<!doctype html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" data-th-content="'登入 - '+${title}" content="">
<meta content="width=device-width, initial-scale=1, user-scalable=no" name="viewport">
<title data-th-text="'登入 - '+${title}">Login | APP NAME</title>
<link rel="shortcut icon" type="image/x-icon" href="" data-th-href="@{/themes/images/favicon.ico}">

<!-- Bootstrap css -->
<link rel="stylesheet" type="text/css" href="" data-th-href="@{/assets/css/bootstrap.min.css}">


</head>

<body class="body-custom">
<div class="logincard">
  	<div class="card-default">
		<div class="login-card">
			<form th:action="@{/login}" id="login-form" method="post">
				<div class="text-center">
					<div class="loginlogo">
						<a href="javascript:void(0);"><img src="" alt="Logo" data-th-src="@{/themes/images/logo-icon.png}"></a>
					</div>
					<h3>登入 <span>到 <strong data-th-text="${title}">APP NAME</strong></span></h3>
				</div>
				
				<div class="pmd-card-body">
					<div class="alert-danger" role="alert" data-th-if="${param.error}!=null">用户名或密码错误,请重新输入</div>
                    <div class="form-group">
                        <label for="inputError1" class="control-label">用户名</label>
                        <div class="input-group">
                            <div class="input-group-addon"><i class="material-icons md-dark">perm_identity</i></div>
                            <input type="text" name="username" class="form-control" id="exampleInputAmount">
                        </div>
                    </div>
                    
                    <div class="form-group">
                        <label for="inputError1" class="control-label">密码</label>
                        <div class="input-group">
                            <div class="input-group-addon"><i class="material-icons md-dark">lock_outline</i></div>
                            <input type="password" name="password" class="form-control" id="exampleInputAmount">
                        </div>
                    </div>
                </div>
				<div class="text-center">
					<a href="javascript:void(0);" id="login-button" type="button" class="btn btn-primary btn-block">登入</a>
                    
				</div>
				
			</form>
		</div>
	</div>
</div>

<!-- Scripts Starts -->
<script src="" data-th-src="@{/assets/js/jquery-1.12.2.min.js}"></script>
<script src="" data-th-src="@{/assets/js/bootstrap.min.js}"></script>
<!-- login page sections show hide -->
<script type="text/javascript">
$(document).ready(function(){
		$('#login-button').click(function(){
            $('#login-form').submit()
        })
});
</script> 

<!-- Scripts Ends -->

</body>
</html>

然后登入后的页面,我们需要在页面显示登入者的名称

<!doctype html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" data-th-content="${title}" content="">
<meta content="width=device-width, initial-scale=1, user-scalable=no" name="viewport">

<title data-th-text="${title}"></title>
<link rel="shortcut icon" type="image/x-icon" href="" data-th-href="@{/themes/images/favicon.ico}">
<!-- Bootstrap css -->
<link rel="stylesheet" type="text/css" href="" data-th-href="@{/assets/css/bootstrap.min.css}" />
</head>

<body>
<div data-th-text="${#authentication.name}"></div>
<div><a href="" data-th-href="@{/logout}">登出</a></div>
</body>
</html>

我们这里使用data-th-text="${#authentication.name}"使div里边显示登入用户的名称

其中#authentication是指向了一个org.thymeleaf.extras.springsecurity4.auth.Authorization的对象实例(org.springframework.security.core.Authentication接口的实现),这里需要引入本文的重点:thymeleaf-extras-springsecurity扩展

thymeleaf-extras-springsecurity扩展是thymeleaf对spring security一个扩展模块,该模块提供一个新的dialect叫作org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect,增加该dialect后,可以在页面上用sec前缀获得很多来自spring security的内容

例如:

<div data-sec-authentication="name"></div>

也是可以在div内显示登入者的名称

更可以获取当前登入用户的角色,从而判断部分内容是否对其显示

<div data-sec-authorize="hasRole('ADMIN')">
        这部分内容当用户角色为为ADMIN时显示
    </div>

具体还可以做什么,可以到该项目的github上边看说明:https://github.com/thymeleaf/thymeleaf-extras-springsecurity

 

Thymeleaf-extras-springsecurity的扩展的集成

现在开始我们在这个项目上引入该扩展的依赖,如果你使用的是spring-security3,记得要改一下artifactId

<!-- thymeleaf的spring security扩展 -->
        <dependency>
		    <groupId>org.thymeleaf.extras</groupId>
		    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
		    <version>${thymeleaf.version}</version>
		</dependency>

再我们之前使用的security配置类里边加入这部分扩展的bean声明

    @Bean
    public SpringResourceTemplateResolver templateResolver(){
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        //配置模板
        templateResolver.setPrefix("classpath:/templates/");
        templateResolver.setSuffix(".html");
        // 使用HTML的模式,也就是支持HTML5的方式,使用data-th-*的H5写法来写thymeleaf的标签语法
        templateResolver.setTemplateMode(TemplateMode.HTML);
        // 之前在application.properties中看到的缓存配置
        templateResolver.setCacheable(true);

        return templateResolver;
    }
    
    @Bean
    public SpringTemplateEngine templateEngine() {
        //模板引擎增加SpringSecurityDialect,让模板能用到sec前缀,获取spring security的内容
        SpringTemplateEngine engine = new SpringTemplateEngine();
        SpringSecurityDialect securityDialect = new SpringSecurityDialect();
        Set<IDialect> dialects = new HashSet<>();
        dialects.add(securityDialect);
        engine.setAdditionalDialects(dialects);
        
        engine.setTemplateResolver(templateResolver());
        //允许在内容中使用spring EL表达式
        engine.setEnableSpringELCompiler(true);
        
        return engine;
    }
    
    //声明ViewResolver
    @Bean
    public ThymeleafViewResolver viewResolver(){
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
        return viewResolver;
    }

大功告成!

以上即是本文所有内容,也是工作中一些记录,希望对看到本文的您有所帮助,少走一些弯路

参考网址:

https://github.com/thymeleaf/thymeleaf-extras-springsecurity

https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html

http://www.thymeleaf.org/doc/articles/springsecurity.html

https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/

展开阅读全文
打赏
1
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
1
分享
返回顶部
顶部