在rbac项目中集成SpringSecurity
前提条件
注释:MvcJavaConfig,CheckLoginInterceptor,CheckPermissionInterceptor失效
* 添加依赖
<!--spring security 组件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
添加依赖之后发现验证码被拦截了。。。
* 添加SecurityConfig配置
SecurityConfig.java
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private CorsFilter corsFilter;
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 不需要认证
.antMatchers("/api/login","/api/code","/api/logout")
.permitAll()
// 所有请求都必须被认证,必须登录后被访问
.anyRequest()
.authenticated();
// 不基于session,关闭csrf
http.csrf().disable();
}
能够完成rbac项目认证功能
* 登录进去(但是没有认证)
* 认证
LoginUser.java
@Data
public class LoginUser implements UserDetails {
@Getter
private Employee employee;
public LoginUser(Employee employee) {
this.employee = employee;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return employee.getPassword();
}
@Override
public String getUsername() {
return employee.getName();
}
/**
* 账户是否未过期,过期无法验证
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 账户是否未过期,过期无法验证
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*/
@Override
public boolean isEnabled() {
return true;
}
}
UserDetailsServiceImpl.java
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private IEmployeeService employeeService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Employee employee = employeeService.selectByUserName(username);
Assert.notNull(employee,"登录用户:"+username + "不存在");
// 构建UserDetails
LoginUser loginUser = new LoginUser(employee);
return loginUser;
}
}
// 注释:username是接收浏览器的用户名
LoginServiceImpl.java
@Service
public class LoginServerImpl implements ILoginServer {
@Autowired
private RedisUtils redisUtils;
@Autowired
private PermissionMapper permissionMapper;
@Autowired
private EmployeeMapper employeeMapper;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public Map<String, String> VerificationCode() {
// 获取验证码
Map<String, String> verifyCode = VerifyCodeUtil.generateVerifyCode();
// 存放到Redis中
String key = Constant.VerifyCode + verifyCode.get("uuid");
String value = verifyCode.get("code");
redisUtils.set(key,value,Constant.VERIFY_CODE_EXPIRES);
// 封装验证码
verifyCode.remove("code");
return verifyCode;
}
@Override
public Employee login(LoginInfoVo loginInfoVo) {
// 1校验参数
Assert.notNull(loginInfoVo,"非法参数");
Assert.hasLength(loginInfoVo.getUsername(),"用户名长度不能为空");
Assert.hasLength(loginInfoVo.getPassword(),"密码长度不能为空");
// 2校验验证码
// 用户传过来的验证码
String userCode = loginInfoVo.getCode();
String UserUuid = loginInfoVo.getUuid();
Assert.notNull(userCode,"验证码不能为空");
// 从Redis中获取验证码code
String key = Constant.VerifyCode+UserUuid;
String code = redisUtils.get(key);
// 校验客户端穿传递过来的验证码
Assert.state(VerifyCodeUtil.verification(userCode,code,true),"验证码不正确");
// // 3登录
// Employee employee = employeeMapper.login(loginInfoVo.getUsername(), loginInfoVo.getPassword());
// // 4校验登录逻辑
// Assert.notNull(employee,"用户名或者密码错误");
// ------springSecurity 认证--------------------------
// try {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginInfoVo.getUsername(), loginInfoVo.getPassword());
Authentication authenticate = authenticationManager.authenticate(token);
// 假如认证失败,会抛出异常
// 正常跑下来,代码认证成功
// TODO:
// token.getPrincipal() --- > string:admin
LoginUser loginUser = (LoginUser)authenticate.getPrincipal(); // loginUser
Employee employee = loginUser.getEmployee();
// }catch (Exception e){
// e.printStackTrace();
// }
// ------springSecurity 认证--------------------------
// 把用户的信息存储到Redis中
// key 设计 rbac:login_employee:emp_id
// value 存储的格式 json字符串
String UserValue = JsonUtils.toJsonString(employee);
String UserKey= Constant.LOGIN_EMPLOYEE_EMPID + employee.getId();
redisUtils.set(UserKey,UserValue);
// 存储当前用户的权限信息到Redis中
// 通过员工表的id找到查询employee_role 找到 roleId,然后通过roleId去找role_permission到对应的permissionId,
// 通过permissionId 查询对应permission表找到对应的权限信息
List<String> selectExpressionByEmpId = permissionMapper.selectExpressionByEmpId(employee.getId());
String PermissionValue = JsonUtils.toJsonString(selectExpressionByEmpId);
String PermissionKey = Constant.LOGIN_PERMISSION_EMPID+employee.getId();
redisUtils.set(PermissionKey,PermissionValue);
return employee;
}
@Override
public void logout(HttpServletRequest request) {
// 从请求头中获取empId
String userId = request.getHeader(Constant.LOGOUT_USER_ID);
// 参数校验
Assert.notNull(userId,"非法参数");
long employeeId = Long.parseLong(userId);
// 清除Redis中的数据
redisUtils.del(Constant.LOGIN_PERMISSION_EMPID+employeeId); // 权限
redisUtils.del(Constant.LOGIN_EMPLOYEE_EMPID+employeeId); // 用户
}
}
SecurityConfig.java
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private CorsFilter corsFilter;
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 不需要认证
.antMatchers("/api/login","/api/code","/api/logout")
.permitAll()
// 所有请求都必须被认证,必须登录后被访问
.anyRequest()
.authenticated();
// 添加跨域过滤器在认证之前
// 添加JWT filter
http.addFilterBefore(jwtAuthenticationTokenFilter,
UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
http.addFilterBefore(corsFilter,JwtAuthenticationTokenFilter.class);
// 不基于session,关闭csrf
http.csrf().disable();
}
// 身份认证接口
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 写UserDetailsService,告诉SS
// 并且要告诉密码器
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
/**
* 解决 无法直接注入 AuthenticationManager
* @return
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager(); // 直接从父取
}
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
现象:此时可以登录,但是有跨域问题和每个请求没有认证问题
* 添加跨域过滤器
MvcJavaConfigV2.java
@Configuration
public class MvcJavaConfigV2 implements WebMvcConfigurer {
// 跨域配置
@Bean
public CorsFilter corsFilter(){
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
// 设置访问源地址
configuration.addAllowedOrigin("*");
// 设置访问同源请求头
configuration.addAllowedHeader("*");
// 设置访问同源请求方法
configuration.addAllowedMethod("*");
// 有效期为1800s
configuration.setMaxAge(1800L);
// 添加映射路径,拦截一切请求
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",configuration);
// 返回新的CorsFilter
return new CorsFilter(source);
}
}
JwtAuthenticationTokenFilter.java
/**
* 这个过滤要添加SS中
* 每个请求都要认证,添加过滤器(JwtAuthenticationTokenFilter.java)
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private RedisUtils redisUtils;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//
String userId = request.getHeader(Constant.LOGOUT_USER_ID);
String employeeJson = redisUtils.get(Constant.LOGIN_EMPLOYEE_EMPID + userId);
if(!StringUtils.isEmpty(employeeJson)){
// holder:袋子
Employee employee=(Employee) JsonUtils.fromJson(employeeJson, Employee.class);
LoginUser loginUser=new LoginUser(employee);
// 告诉SS,我认证过了
UsernamePasswordAuthenticationToken token=
new UsernamePasswordAuthenticationToken(loginUser,loginUser.getPassword(),loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(token);
}
filterChain.doFilter(request,response);
}
}
SecurityConfig.java
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private CorsFilter corsFilter;
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 不需要认证
.antMatchers("/api/login","/api/code","/api/logout")
.permitAll()
// 所有请求都必须被认证,必须登录后被访问
.anyRequest()
.authenticated();
// 添加跨域过滤器在认证之前
// 添加JWT filter(每个请求都要认证,添加过滤器(JwtAuthenticationTokenFilter))
http.addFilterBefore(jwtAuthenticationTokenFilter,
UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
http.addFilterBefore(corsFilter,JwtAuthenticationTokenFilter.class);
// 不基于session,关闭csrf
http.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 写UserDetailsService,告诉SS
// 并且要告诉密码器
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
/**
* 解决 无法直接注入 AuthenticationManager
* @return
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager(); // 直接从父取
}
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
现象:现在是可以登录,并且可以得到认证…