这一篇主要是对SpringSecurity的知识点梳理。
之前项目里面使用了一套登录验证,逻辑上与SpringSecurity的类似,基于WebService接口访问,我的那种是基于Token实现每次验证,通过Role确定方法的访问权限,在这里进行一个SpringSecurity的实现以及扩展。
一 前言
整个梳理包括以下几个部分
- 开启security,并且配置访问
- 自定义登录成功 / 失败 处理器
- 定义自定义拦截器
- 定义方法访问权限
二 详情
2.1 开启springSecurity 支持
2.1.1 依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
。。。。省略。。。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.1.2 开启springSecurity支持及配置
这里主要是基于数据库的账户查询,所以先说相关的实体类。
主要有两个User 和Roles
@Entity
public class Users implements UserDetails {
@Id
@GeneratedValue
private Long id;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@ManyToMany(cascade = {CascadeType.REFRESH}, fetch = FetchType.EAGER)
private List<Roles> roles;
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
List<Roles> roles = this.getRoles();
for (Roles role : roles) {
auths.add(new SimpleGrantedAuthority(role.getName()));
}
return auths;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
。。。省略getter / setter
}
注意其中的方法:
方法 | 作用 |
---|---|
getAuthorities() | |
isAccountNonExpired() | 账户未过期 |
isAccountNonLocked() | 账户未锁定 |
isCredentialsNonExpired() | 证书未过期 |
isEnabled() | 账户已启用 |
当不准备手动配置的时候,这四项应该都返回的是True
Roles就比较简单了
@Entity
public class Roles {
@Id
@GeneratedValue
private Long id;
private String name;
。。。省略getter / setter
}
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
类上的操作主要有两个,使用@EnableWebSecurity注解以及继承WebSecurityConfigurerAdapter。
然后说说里面的配置方法
方法 | 作用 |
---|---|
protected void configure(AuthenticationManagerBuilder auth) | |
protected void configure(HttpSecurity http) | |
public void configure(WebSecurity web) |
看一下方法
2.1.3 对springSecurity进行配置
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler myAuthenctiationFailureHandler;
@Bean
public UserService CustomerUserService() {
System.out.print("step1============");
return new UserService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//该方法用于用户认证,此处添加内存用户,并且指定了权限
// auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
// .withUser("user1").password(new BCryptPasswordEncoder().encode("123456")).roles("USER")
// .and()
// .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("ADMIN");
auth.userDetailsService(CustomerUserService()).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//此方法中进行了请求授权,用来规定对哪些请求进行拦截
//其中:antMatchers--使用ant风格的路径匹配
//regexMatchers--使用正则表达式匹配
http.authorizeRequests()
.antMatchers("/test/**").permitAll()
.antMatchers("/before/**").permitAll()
.antMatchers("/index").permitAll()
.antMatchers("/").permitAll() //以上是ant风格的过滤器
.anyRequest().authenticated() //其它请求都需要校验才能访问
.and()
.formLogin() //定义表单登录
.loginPage("/login") //定义登录的页面"/login",允许访问
.defaultSuccessUrl("/home") //登录成功后默认跳转到"list"
.successHandler(myAuthenticationSuccessHandler) //定义成功处理函数
.failureHandler(myAuthenctiationFailureHandler) //定义失败处理函数
.permitAll()
.and()
.logout() //默认的"/logout", 允许访问
.logoutSuccessUrl("/index")
.permitAll();
//进行自定义filter
http.addFilterBefore(new BeforeFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Override
public void configure(WebSecurity web) throws Exception {
//解决静态资源被拦截的问题
web.ignoring().antMatchers("/**/*.js", "/lang/*.json", "/**/*.css", "/**/*.js", "/**/*.map", "/**/*.html", "/**/*.png");
}
}
2.1.4 自定义filter
当我们需要知道请求的拦截内容的时候可以通过自定义filter来实现
通过继承GenericFilterBean 重写方法实现
public class BeforeFilter extends GenericFilterBean {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
logger.info("This is a filter before UsernamePasswordAuthenticationFilter.");
logger.info("getRemoteHost:{}",servletRequest.getRemoteHost());
logger.info("getContentType:{}",servletRequest.getContentType());
logger.info("getLocalName:{}",servletRequest.getLocalName());
logger.info("getLocalAddr:{}",servletRequest.getLocalAddr());
logger.info("getServerName:{}",servletRequest.getServerName());
logger.info("getServerName:{}",servletRequest.getRemoteAddr());
logger.info("getServerName:{}",servletRequest.getServletContext());
// CommonUtils.reflect(servletRequest);
// 继续调用 Filter 链
filterChain.doFilter(servletRequest, servletResponse);
}
}
2.1.5 定义成功已经失败处理器
登录失败处理器
@Component("myAuthenctiationFailureHandler")
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
// AuthenticationException 认证过程中产生的异常
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
logger.info("MyAuthenticationSuccessHandler login failure!");
logger.info(exception.getMessage());
logger.info(exception.getLocalizedMessage());
exception.printStackTrace();
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
}
}
登录成功处理器
@Component("myAuthenticationSuccessHandler")
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ObjectMapper objectMapper;
@Autowired
private UserLogService logService;
@Autowired
private UserService userService;
// Authentication 封装认证信息
// 登录方式不同,Authentication不同
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
logger.info("MyAuthenticationSuccessHandler login success!");
response.setContentType("application/json;charset=UTF-8"); // 把authentication对象转成 json 格式 字符串 通过 response 以application/json;charset=UTF-8 格式写到响应里面去
response.getWriter().write(objectMapper.writeValueAsString(authentication));
logger.info((String)request.getSession().getAttribute("username"));
// Users user = userService.findUserByName((String)request.getSession().getAttribute("username"));
// CommonUtils.reflect(user);
// logService.save(new UserLog(String.valueOf(user.getId()),user.getUsername(),"名称为" +user.getUsername() + "的用户登录成功,登录IP为" + request.getRemoteAddr(),new Date()));
// 父类的方法 就是 跳转
super.onAuthenticationSuccess(request, response, authentication);
}
}
参考:
https://www.cnkirito.moe/spring-security-1/
http://www.spring4all.com/article/419