SpringSecurity代码核心

认证过滤器:通过attemptAuthentication方法进行认证,获取请求的账号密码为user对象,通过AuthenticationManager的authenticate方法,使用自己编写的UserDetailService进行认证,认证成功则调用successfulAuthentication方法,将用户的权限存储在redis中,返回token

public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private RedisTemplate redisTemplate;
    private AuthenticationManager authenticationManager;
    private TokenManager tokenManager;

    public TokenLoginFilter(TokenManager tokenManager, RedisTemplate redisTemplate, AuthenticationManager authenticationManager) {
        this.redisTemplate = redisTemplate;
        this.authenticationManager = authenticationManager;
        this.tokenManager = tokenManager;
        this.setPostOnly(false);//只接受POST请求(否则调用认证失败方法)
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login", "POST"));
    }

    //获取表单提交用户名密码
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //获取表单数据
        User user = null;
        try {
            user = new ObjectMapper().readValue(request.getInputStream(), User.class);
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }


    }

    //认证成功
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //认证成功后用户信息
        SecurityUser user = (SecurityUser) authResult.getPrincipal();

        String username = tokenManager.generateToken(user.getCurrentUserInfo().getUsername());
        String token = tokenManager.generateToken(username);
        redisTemplate.opsForValue().set(username, user.getPermissionValueList());

        ResponseUtil.out(response, R.ok().data("token", token));
    }

    //认证失败

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        ResponseUtil.out(response, R.error());
    }
}


Security配置类

public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {

    private RedisTemplate redisTemplate;

    private TokenManager tokenManager;

    private DefaultPasswordEncoder defaultPasswordEncoder;

    private UserDetailsService userDetailsService;

    @Autowired
    public TokenWebSecurityConfig(RedisTemplate redisTemplate,TokenManager tokenManager,DefaultPasswordEncoder defaultPasswordEncoder, UserDetailsService userDetailsService){
        this.redisTemplate = redisTemplate;
        this.tokenManager = tokenManager;
        this.defaultPasswordEncoder = defaultPasswordEncoder;
        this.userDetailsService = userDetailsService;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                .authenticationEntryPoint(new UnauthEntryPoin())//没有权限访问
                .and().csrf().disable()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and().logout().logoutUrl("/admin/acl/index/logout")
                .addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate))
                .and().addFilter(new TokenAuthFilter(authenticationManager() ,redisTemplate,tokenManager))
                .addFilter(new TokenLoginFilter(tokenManager,redisTemplate,authenticationManager())).httpBasic();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/api/**");//不进行认证的路径,可以直接访问
    }
}

密码编码译码器

@Component
public class DefaultPasswordEncoder implements PasswordEncoder {


    public DefaultPasswordEncoder(){
        this(-1);
    }

    public DefaultPasswordEncoder(int i){

    }

    @Override
    public String encode(CharSequence var1) {

        return MD5.encrypt(var1.toString());
    }


    //和密码比对
    //var1为密码,var2位加密后的密码(MD5是不可逆的)
    @Override
    public boolean matches(CharSequence var1, String var2) {
        return var2.equals(MD5.encrypt(var1.toString()));
    }
}

Token管理器

@Component
public class TokenManager {
    //有效时长
    private long expiraTime = 24*60;

    //编制密钥
    private String tokenSecretKey = "123456";


    //根据用户名生成token
    public String generateToken(String username){
        String token = Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis()+expiraTime))
                .signWith(SignatureAlgorithm.ES512,tokenSecretKey)
                .compressWith(CompressionCodecs.GZIP).compact();

        return token;
    }

    //根据token获取用户信息
    public String getUserInfoByToken(String token){
        String userInfo = Jwts.parser().setSigningKey(tokenSecretKey)
                .parseClaimsJws(token).getBody()
                .getSubject();

        return userInfo;
    }

    public void removeToken(String token){

    }
}

无权访问入口

public class UnauthEntryPoin implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        ResponseUtil.out(httpServletResponse, R.error());
    }
}

登出处理器:获取请求头携带的token,移除token,redis删除token对应的用户

public class TokenLogoutHandler implements LogoutHandler {


    private TokenManager tokenManager;


    private RedisTemplate redisTemplate;

    public TokenLogoutHandler(TokenManager tokenManager,RedisTemplate redisTemplate){
        this.redisTemplate = redisTemplate;
        this.tokenManager = tokenManager;
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        //从header获取token
        String token = request.getHeader("token");


//        token不为空   移除token
        if(null != token){
            tokenManager.removeToken(token);

            String userInfo = tokenManager.getUserInfoByToken(token);
            redisTemplate.delete(userInfo);
        }
        ResponseUtil.out(response, R.ok());
    }
}

授权过滤器:根据请求头的token获取权限列表,生成一个授权对象,如果对象不为空,则放在上下文对象SecurityContext中,他是全局生效。对于@Security放在控制器方法的注解,会根据权限进行验证

public class TokenAuthFilter extends BasicAuthenticationFilter {

    private RedisTemplate redisTemplate;

    private TokenManager tokenManager;


    public TokenAuthFilter(AuthenticationManager authenticationManager, RedisTemplate redisTemplate, TokenManager tokenManager) {
        super(authenticationManager);
        this.redisTemplate = redisTemplate;
        this.tokenManager = tokenManager;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        //获取当前认证的用户权限信息
        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
       //如果有权限信息,就放在权限上下文
        if(authentication != null){
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        //放行
        chain.doFilter(request,response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader("token");
        if (token != null) {
            //从token获取用户名
            String username = tokenManager.getUserInfoByToken(token);

            //从redis获取权限列表
            List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(username);
            Collection<GrantedAuthority> authrity = new ArrayList<>();
            for (String s : permissionValueList) {
                authrity.add(new SimpleGrantedAuthority(s));
            }

            return new UsernamePasswordAuthenticationToken(username,token,authrity);
        }
        return null;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值