由于最近项目需要使用activiti工作流,activiti7中使用SpringSecurity安全框架。但是项目的业务场景是不涉及登录,登录在统一权限系统中。
任务要求是用户访问系统发送请求时带了token,不需要将页面跳转到登录页面进行登录,直接通过请求时的用户信息进行登录Spring Security验证用户信息。
网上大部分资料都是spring security配置资料中都涉及登录页面的的登录,基本上没有这块资料。中间发现很多坑,自己在此记录一下。
自己的项目框架如下:
1.springboot 2.2.1.RELEASE
2.activiti 7.1.0.M1
3.数据库mysql
参考自己之前地址:项目配置链接 一、创建拦截 创建SessionInterceptorConfig和MyInterceptor 两个类
SessionInterceptorConfig代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SessionInterceptorConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor sessionInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 配置会话拦截器,不拦截应用登录入口和应用注销入口
registry.addInterceptor(sessionInterceptor).addPathPatterns("/**")
.order(Ordered.HIGHEST_PRECEDENCE);
}
}
MyInterceptor 代码如下
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object apiResponsehandler)
throws Exception {
//截取请求header中token,解析用户信息业务代码不做粘贴
//以下做springSecurity
//通过token 兑换用户信息 将用户名和密码生成可用的AuthenticationToken
//这是SpringSecurity的
UsernamePasswordAuthenticationToken springSecurityAuthToken = new UsernamePasswordAuthenticationToken(user.getUserLoginName(), user.getUserLoginName());
//确保request 中有sessionId
String sessionId = request.getSession().getId();
logger.info("sessionId=="+sessionId);
//设置AuthenticationToken 的userDetails主要获取请求中一些参数信息
//注意该方法request中必须有session信息否则会报错
springSecurityAuthToken.setDetails(new WebAuthenticationDetails(request));
//用户信息Token完成SpringSecurity认证获得认证对象
Authentication authenticatedUser = authenticationManager.authenticate(springSecurityAuthToken);
//将用户存放到安全框架SecurityContext中
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
//SecurityContext context = SecurityContextHolder.getContext();
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
Object attribute = request.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
return true;
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
}
只配置这些启动还是有问题,会出现401
二、自定义 Spring Boot Web 安全配置类
创建SecurityConfig类
代码如下:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().permitAll().and().logout().permitAll()//配置不需要登录验证
.and().headers().frameOptions().sameOrigin()// 解决 frame because it set 'X-Frame-Options' to 'deny'
.and().cors() //允许跨域
.and().csrf().disable() //关闭跨站请求伪造
;
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
@Bean
@Override
protected UserDetailsService userDetailsService(){
//采用一个自定义的实现UserDetailsService接口的类
return new MyUserDetailServiceImpl();
}
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider
= new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
//默认是BCryptPasswordEncoder 加密 但是业务已经不需要再做密码要求,必须采用统一权限系统解密方式不现实,改为NoOpPasswordEncoder 否则报密码方式问题
@Bean
public PasswordEncoder encoder() {
return NoOpPasswordEncoder.getInstance();
}
}
创建UserDetailsService实现类 (必须)
代码如下:
/**
*
* 自定义实现SpringSecurity 中的UserDetailsService接口
* @author jiaoda
* @version 1.0
*/
@Service
public class MyUserDetailServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//这里通过用户名查询用户信息
System.out.println("s==="+s);
//自己实现代码
return new MyUserPrincipal(contextUser);
}
}
创建MyUserPrincipal实现类
代码如下:
/**
*
* 自定义实现SpringSecurity 中的UserDetails接口
* @author jiaozhicong
* @version 1.0
*/
public class MyUserPrincipal implements UserDetails {
public ContextUser user;
public MyUserPrincipal(ContextUser user) {
this.user = user;
}
public ContextUser getUser() {
return user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return user.getUserLoginName();
}
@Override
public String getUsername() {
return user.getUserName();
}
//默认false
/*
*必须改为true 否则用户无效
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/*
*必须改为true 否则用户无效
*/
//默认false
@Override
public boolean isAccountNonLocked() {
return true;
}
/*
*必须改为true 否则用户无效
*/
//默认false
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/*
*必须改为true 否则用户无效
*/
//默认false
@Override
public boolean isEnabled() {
return true;
}
}
到此为止,在启动项目测试不会出现问题。