在rbac项目中集成SpringSecurity

在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();
    }


}

现象:现在是可以登录,并且可以得到认证…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值