Spring Boot整合Spring Security
Spring Boot对于该家族的框架支持良好,但是当中本人作为小白配置还是有一点点的小问题,这里分享一下。这个项目是使用之前发布的Spring Boot会员管理系统重新改装,将之前filter登录验证改为Spring Security
1. 配置依赖
Spring Boot框架整合Spring Security只需要添加相应的依赖即可,其后都是配置Spring Security。
这里使用Maven开发,依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
**2. 配置Spring Security **
**2.1 定制WebSecurityConfigurerAdapter **
下面通过Java配置Spring Security不拦截静态资源以及需要登录验证等各种信息
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启方法上的认证
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private CustomerUserDetailsService userDetailsService;
@Resource
private CustomerLoginSuccessHandler successHandler;
@Resource
private BCryptPasswordEncoder encoder;
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**"); //不过滤静态资源
super.configure(web);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService) //注册自己定制的UserDetailsService
.passwordEncoder(encoder); // 配置密码加密器
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //获取请求方面的验证器
.antMatchers("/", "/error").permitAll()// 访问当前配置的路径可通过认证
//访问其他路径需要认证和角色权限
.anyRequest().hasAnyAuthority(AdminRole.G_ADMIN.toString(), AdminRole.S_ADMIN.toString())
.anyRequest().authenticated()
.and()
.formLogin() //获取登录认证验证器
.loginPage("/login") //注册自定义的登录页面URL
.failureForwardUrl("/login") //登录失败后以登录时的请求转发到该链接
.successHandler(successHandler) //登录成功后调用该处理器
.permitAll() //登录请求给予通过认证
.and()
.logout() //推出登录
.logoutSuccessUrl("/login") //退出后访问URL
.and()
.csrf().disable(); //关闭csrf,默认开启
}
}
上面的类是用于配置Spring Security框架的,这里有关于几个东西要说明下:
@EnableGlobalMethodSecurity
这是用于配置类 / 方法上的安全认证,它默认关闭了,我们现在配置了@EnableGlobalMethodSecurity(prePostEnabled = true)这将使得可以在方法上使用注解@PreAuthorize("hasAnyAuthority('S_ADMIN')")(使用范例可看AdminController)在没调用注解下的方法时判断当前认证用户有没有S_ADMIN角色权限操作该方法。
这个注解可以使得在开发非Web项目时起到作用。configure(AuthenticationManagerBuilder auth)
这里可以配置该项目与用户的关联,也称作认证。我们需要构建自己的登录验证器,这里我们自定义了一个UserDetailsService和一个密码加密器。**configure(HttpSecurity http) **
这里可以配置我们对于一个HttpSecurity需要通过什么样子的安全认证。代码中还包含了csrf().disable(),这是因为框架默认开启了csrf,这样我们的ajax和表单提交等都需要提供一个token,为了偷懒,所以,你懂的
还有就是一个小tip,这是一个配置,相当于xml,Spring只会加载一次,所以以上的方法Spring是初始化一次的
2.2 定制UserDetailsService
在上面中我们注册了一个自定义的UserDetailsService,这是用于当用户认证时我们需要提供一个可被框架识别的UserDetails,用这个UserDetails和我们的登录用户实体类建立起一个关联,让框架可以处理我们的用户信息,框架所提供的只是username和password,它只是帮助我们认证我们的用户和密码是否匹配。定制UserDetailsService代码如下:
@Component //注册为Spring组件
public class CustomerUserDetailsService implements UserDetailsService{
@Resource
private AdminDao adminDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//通过dao查找当前用户名对应的用户
Admin admin = adminDao.findAdminByUsername(username);
if (admin == null){
throw new UsernameNotFoundException("This username: "+username+"is not exist");
}
//返回一个定制的UserDetails
//AuthorityUtils.createAuthorityList(admin.getRole())就是将我们该用户所有的权限(角色)生成一个集合
return new CustomerUserDetails(admin, AuthorityUtils.createAuthorityList(admin.getRole()));
}
}
当我们登录时,org.springframework.security.authentication.dao.DaoAuthenticationProvider会调用loadUserByUsername方法并且把当前需要认证的用户名传入,我们需要的就是放回一个通过该用户名得出的拥有密码信息的UserDetails,这样,框架就可以帮助我们验证需要认证的密码与查出的密码是否匹配。
2.3 定制UserDetails
要让Spring Security能够识别我们定制的UserDetails,那就需要按照它的标准来写,所以,我们需要实现一个UserDetails接口,具体代码如下:
public class CustomerUserDetails implements UserDetails {
private Admin admin = null;
//存放权限的集合
private final Collection<? extends GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;
public CustomerUserDetails(Admin admin, Collection<? extends GrantedAuthority> authorities) {
this(admin, true, true, true,true,authorities);
}
public CustomerUserDetails(Admin admin, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
if(admin.getUsername() != null && !"".equals(admin.getUsername()) && admin.getPassword() != null) {
this.admin = admin;
this.enabled = enabled;
this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = authorities;
} else {
throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
}
}
public Admin getAdmin() {
return admin;
}
public void setAdmin(@NotNull Admin admin) {
this.admin = admin;
}
public boolean equals(Object rhs) {
return rhs instanceof CustomerUserDetails && this.getUsername().equals(((CustomerUserDetails) rhs).getUsername());
}
public int hashCode() {
return this.getUsername().hashCode();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return this.admin.getPassword();
}
@Override
public String getUsername() {
return this.admin.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return this.accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return this.accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}
关于以上的写法是参照Spring Security所提供的User类来改写的,具体代码可看org.springframework.security.core.userdetails.User源码。
在org.springframework.security.core.userdetails.User中重写hashCode和equals可能是为了判断重复登录的问题,当然了,这只是个人意淫,纯属瞎猜。为了安全起见我也跟着重写了那两个方法了。
在定制UserDetails我加入了一个成员变量admin,这是因为之前的开发中没有使用该框架,只是使用了filter登录验证,将登录信息存放到 session,所以,为了不大改之前的旧东西,所以我还将会从这里获取admin存于session
2.4 定制AuthenticationSuccessHandler
定制AuthenticationSuccessHandler不是直接继承接口,而是继承一个实现类SavedRequestAwareAuthenticationSuccessHandler,因为这里保存了我们的登录前请求信息,我们可以不用获取RequestCache就可实现直接从登录页面重定向到登录前访问的URL,具体代码如下:
@Component
public class CustomerLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//SecurityContextHolder是Spring Security的核心组件,可获取框架爱内的一些信息
//这里我得到登录成功后的UserDetails
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
request.getSession().setAttribute("admin", ((CustomerUserDetails) principal).getAdmin());
}
super.onAuthenticationSuccess(request, response, authentication);
}
}
关于上面代码中的框架核心组件,可以到官方文档中的此链接查看
定制这个处理器的主要目的是因为我想要偷懒,因为我模板引擎需要访问admin来获取用户名,当然了也可以使用该表达式${session.SPRING_SECURITY_CONTEXT.authentication.principal.username}来获取UserDetails的用户名
3. 总结
写到这里就差不多该结束了,这里我就做下个人总结。这次整合Spring Security中途不是像这篇文章那样如此下来的,中间磕磕碰碰,所以呢,我就在想,是不是我只是停留在了应用层面才导致的这么个结果,如果了解源码多一点就可能不会出现这些问题了。
以上的代码已上传GitHub
可参考链接
https://docs.spring.io/spring-security/site/docs/5.0.3.RELEASE/reference/htmlsingle/
https://blog.csdn.net/u283056051/article/details/55803855
http://www.tianshouzhi.com/api/tutorials/spring_security_4/250
Spring Boot整合Spring Security的更多相关文章
- Spring Boot 整合Spring Data JPA
Spring Boot整合Spring Data JPA 1)加入依赖 <dependency> <groupId>org.springframework.boot</g ...
- Spring boot 整合spring Data JPA+Spring Security+Thymeleaf框架(上)
近期上班太忙所以耽搁了给大家分享实战springboot 框架的使用. 以下是spring boot 整合多个框架的使用. 首先是准备工作要做好. 第一 导入框架所需的包,我们用的事maven 进行 ...
- Spring Boot整合Spring Security自定义登录实战
本文主要介绍在Spring Boot中整合Spring Security,对于Spring Boot配置及使用不做过多介绍,还不了解的同学可以先学习下Spring Boot. 本demo所用Sprin ...
- Spring Boot整合Spring Security总结
一.创建Spring Boot项目 引入Thymeleaf和Web模块以及Spring Security模块方便进行测试,先在pom文件中将 spring-boot-starter-security ...
- Spring Boot 整合 Spring Security,用户登录慢
场景 Spring Boot + Spring Security搭建一个Web项目. 临时用了inMemoryAuthentication. @EnableWebSecurity public cla ...
- Spring Boot整合Spring Batch
引言 Spring Batch是处理大量数据操作的一个框架,主要用来读取大量数据,然后进行一定的处理后输出指定的形式.比如我们可以将csv文件中的数据(数据量几百万甚至几千万都是没问题的)批处理插入保 ...
- Spring Boot整合Spring Session实战
传统java web应用session都是由应用服务器(如tomcat)保存在内存中,这对应但节点应用来说没问题:但对于应用集群来说会造成各节点之间的session无法共享,一个节点挂掉后,其他节点接 ...
- Spring Boot 整合Spring Security 和Swagger2 遇到的问题小结
How to configure Spring Security to allow Swagger URL to be accessed without authentication @Configu ...
- spring boot整合spring Data JPA和freemarker
1.spring Data JPA简介 是一个替代hibernate的一个作用于数据库的框架. 2.整合 1.导入依赖 <dependency> <groupId>org.sp ...
随机推荐
- 使用mysql将备份的sql文件导入到数据库
一.背景 承接上一篇文章<如何使用mysqldump备份数据库>,数据库备份后将用于恢复或者在多个测试环境上迁移.下面描述如何通过批处理文件实现数据加载恢复. 二.环境准备 跟上一篇一样, ...
- 分布式架构设计(一) --- 面向服务的体系架构 SOA
1.1 基于TCP协议的RPC 1.1.1 RPC名词解释 RPC的全称是Remote Process Call,即远程过程调用,RPC的实现包括客户端和服务端,即服务调用方和服务提供方.服务调用方发 ...
- 洛谷 P1025 数的划分
题目描述 将整数n分成k份,且每份不能为空,任意两个方案不相同(不考虑顺序). 例如:n=7,k=3,下面三种分法被认为是相同的. 1,1,5; 1,5,1; 5,1,1; 问有多少种不同的分法. 输 ...
- WordPress制作一个首字母排序的标签页面
很早就想制作这样一个页面了,废话不多说, 先看看效果:传送门 在网上找了很多的代码,试了很久,修改了一些代码,最终就达到了现在的效果. 实现方法:(里面增加了缓存功能,打开页面更快,对数据进行了缓存, ...
- 20165230 2017-2018-2 《Java程序设计》第2周学习总结
20165230 2017-2018-2 <Java程序设计>第2周学习总结 教材学习内容总结 本周学习了JAVA中的数据类型.数组.运算符.表达式和语句,与C语言很类似,二者也有区别. ...
- Elasticsearch就这么简单
一.前言 最近有点想弄一个站内搜索的功能,之前学过了Lucene,后来又听过Solr这个名词.接着在了解全文搜索的时候就发现了Elasticsearch这个,他也是以Lucene为基础的. 我去搜了几 ...
- 常用的Oracle函数收集
to_char(); count(); avg(); sum(); to_date('时间','格式'); NVL(,); NVL2(); substr(); case when then ...
- 通向架构师的道路之 Tomcat 性能调优
一.总结前一天的学习 从"第三天"的性能测试一节中,我们得知了决定性能测试的几个重要指标,它们是: 吞吐量 Responsetime Cpuload MemoryUsage 我们也 ...
- Java 泛型进阶
擦除 在泛型代码内部,无法获得任何有关泛型参数类型的信息. 例子1: //这个例子表明编译过程中并没有根据参数生成新的类型 public class Main2 { public static voi ...
- MYSQL数据库学习十四 存储过程和函数的操作
14.1 为什么使用存储过程和函数 一个完整的操作会包含多条SQL语句,在执行过程中需要根据前面SQL语句的执行结果有选择的执行后面的SQL语句. 存储过程和函数的优点: 允许标准组件式编程,提高了S ...