​springsecurity配合token进行权限控制

springsecurity配合token进行权限控制

Gitee地址:https://gitee.com/tansty/springboot-permission-control

参考别人的博客:https://blog.csdn.net/u012702547/article/details/89629415

一、spring security 简介

spring security 的核心功能主要包括:

  • 认证 (你是谁)- 授权 (你能干什么)- 攻击防护 (防止伪造身份)原理图

<img src=”https://img-blog.csdnimg.cn/172aca5367cc408b8072054d904b331d.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3RhbnN0eV96aA==,size_16,color_FFFFFF,t_70″ alt=”在这里插入图片描述”>

二、代码

1.项目maven依赖

&lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-data-jdbc&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-configuration-processor&lt;/artifactId&gt;
            &lt;optional&gt;true&lt;/optional&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;mysql&lt;/groupId&gt;
            &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.alibaba&lt;/groupId&gt;
            &lt;artifactId&gt;druid-spring-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;1.1.10&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- 引入阿里数据库连接池 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.alibaba&lt;/groupId&gt;
            &lt;artifactId&gt;druid&lt;/artifactId&gt;
            &lt;version&gt;1.1.21&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
            &lt;artifactId&gt;lombok&lt;/artifactId&gt;
            &lt;optional&gt;true&lt;/optional&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;!--mybatis-plus--&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.baomidou&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;3.4.1&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.baomidou&lt;/groupId&gt;
            &lt;artifactId&gt;mybatis-plus-generator&lt;/artifactId&gt;
            &lt;version&gt;3.4.1&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- 模板引擎 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.velocity&lt;/groupId&gt;
            &lt;artifactId&gt;velocity-engine-core&lt;/artifactId&gt;
            &lt;version&gt;2.0&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.aspectj&lt;/groupId&gt;
            &lt;artifactId&gt;aspectjweaver&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;ma.glasnost.orika&lt;/groupId&gt;
            &lt;artifactId&gt;orika-core&lt;/artifactId&gt;
            &lt;version&gt;1.5.2&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;io.springfox&lt;/groupId&gt;
            &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
            &lt;version&gt;2.9.2&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;io.springfox&lt;/groupId&gt;
            &lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;
            &lt;version&gt;2.9.2&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;io.jsonwebtoken&lt;/groupId&gt;
            &lt;artifactId&gt;jjwt&lt;/artifactId&gt;
            &lt;version&gt;0.9.1&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.codehaus.jackson&lt;/groupId&gt;
            &lt;artifactId&gt;jackson-mapper-asl&lt;/artifactId&gt;
            &lt;version&gt;1.9.13&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.alibaba&lt;/groupId&gt;
            &lt;artifactId&gt;fastjson&lt;/artifactId&gt;
            &lt;version&gt;1.2.75&lt;/version&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;

2.spring-security的配置

(1)认证类

springsecurity自定义认证规则,用户的登录就会走这个类去认证,并且在这里进行角色权限的赋予

/**
 * description: 用户认证 &lt;br&gt;
 * date: 2021/8/1 15:14 &lt;br&gt;
 * author: ztz &lt;br&gt;
 * version: 1.0 &lt;br&gt;
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {<!-- -->
    @Autowired
    UserMapper userMapper;
    @Autowired
    MyPasswordEncoder passwordEncoder ;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {<!-- -->
        // 根据用户名去数据库中查询
        User user = userMapper.selectOne(new QueryWrapper&lt;User&gt;().eq("user_name", username));
        if(user==null){<!-- -->
            return null;
        }else {<!-- -->
            Collection&lt;GrantedAuthority&gt; authorities = new ArrayList&lt;&gt;();
            // 数据库中查询出所拥有的角色进行赋予
            for (Role role : userMapper.findRoleByUsername(username)) {<!-- -->
                authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
            }
            return new JwtUser(user,authorities);
        }
    }

}

(2)自定义加密规则

可以自定义Encoder规则,只要实现PasswordEncoder这个接口并且在配置类中使用这个加密类就好

/**
 * description: 自定义加密规则  &lt;br&gt;
 * date: 2021/8/1 18:22 &lt;br&gt;
 * author: ztz &lt;br&gt;
 * version: 1.0 &lt;br&gt;
 * @author Tansty
 */
@Component
public class MyPasswordEncoder implements PasswordEncoder {<!-- -->
    // 加密
    @Override
    public String encode(CharSequence rawPassword) {<!-- -->
        return MD5Util.encode((String) rawPassword) ;
    }
	// 匹配
    @Override
    public boolean matches(CharSequence rawPassword, String encodePassword) {<!-- -->
        return  (MD5Util.encode( (String) rawPassword)).equals(encodePassword);
    }

}

(3)核心配置类

/**
 * description: Security配置 &lt;br&gt;
 * date: 2021/7/31 18:15 &lt;br&gt;
 * author: ztz &lt;br&gt;
 * version: 1.0 &lt;br&gt;
 */
@Configuration
@EnableWebSecurity
//开启方法权限注解支持
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {<!-- -->
    @Autowired
    UserDetailsServiceImpl userDetailsService;
    /**
     * @Description 认证
     * @author Tansty
     * @date 2021/8/3 14:14
     * @param auth
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {<!-- -->
       auth.userDetailsService(userDetailsService).passwordEncoder(new MyPasswordEncoder());
    }
    /**
     * @Description 配置静态资源的过滤
     * @author Tansty
     * @date 2021/8/3 14:14
     * @param web
     */
    @Override
    public void configure(WebSecurity web) throws Exception {<!-- -->
        // 配置静态文件不需要认证
        web.ignoring().antMatchers(
                "/swagger-ui.html",
                // swagger api json
                "/v2/api-docs",
                // 用来获取支持的动作
                "/swagger-resources/configuration/ui",
                // 用来获取api-docs的URI
                "/swagger-resources",
                // 安全选项
                "/swagger-resources/configuration/security",
                "/swagger-resources/**",
                "/webjars/**",
                "/druid/**"
        );
    }

    /**
     * @Description 授权
     * @author Tansty
     * @date 2021/8/3 14:14
     * @param http
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {<!-- -->
        http.cors().and().csrf().disable()
                .authorizeRequests()
                .antMatchers("/auth/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                // 不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
                // 添加无权限时的处理
                .accessDeniedHandler(new JWTAccessDeniedHandler());
    }

}

(4)token的拦截器

1.用户登录及token的授予
/**
 * description: 用户登录及token的授予 &lt;br&gt;
 * date: 2021/8/5 13:19 &lt;br&gt;
 * author: ztz &lt;br&gt;
 * version: 1.0 &lt;br&gt;
 */

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {<!-- -->
    private ThreadLocal&lt;Integer&gt; rememberMe = new ThreadLocal&lt;&gt;();
    private AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {<!-- -->
        this.authenticationManager = authenticationManager;
        // 在这里可以自定义登录的路径
        super.setFilterProcessesUrl("/auth/login");
    }
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {<!-- -->
        // 从输入流中获取到登录的信息
        try {<!-- -->
            LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class);
            //rememberMe.set(loginUser.getRememberMe() == null ? 0 : loginUser.getRememberMe());
            System.out.println(loginUser);
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList&lt;&gt;())
            );
        } catch (IOException e) {<!-- -->
            e.printStackTrace();
            return null;
        }
    }

    // 成功验证后调用的方法
    // 如果验证成功,就生成token并返回
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {<!-- -->
        JwtUser jwtUser = (JwtUser) authResult.getPrincipal();
        System.out.println("jwtUser:" + jwtUser.toString());
        String role = "";
        Collection&lt;? extends GrantedAuthority&gt; authorities = jwtUser.getAuthorities();
        for (GrantedAuthority authority : authorities){<!-- -->
            role = authority.getAuthority();
            System.out.println(role);
        }
        String token = JwtTokenUtils.createToken(jwtUser.getUsername(), role, 0);
        System.out.println(token);
        // 返回创建成功的token
        // 但是这里创建的token只是单纯的token
        // 按照jwt的规定,最后请求的时候应该是 `Bearer token`
        //RedisUtil redisUtil = new RedisUtil();
        //redisUtil.set(jwtUser.getUsername(),JwtTokenUtils.TOKEN_PREFIX + token);
        //redisUtil.expire(jwtUser.getUsername(),JwtTokenUtils.EXPIRATION);
        response.setHeader("token", JwtTokenUtils.TOKEN_PREFIX + token);
    }
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {<!-- -->
        response.getWriter().write("authentication failed, reason: " + failed.getMessage());
    }

}

2.用户携带token时的验证
/**
 * description: 用户携带token时的验证  &lt;br&gt;
 * date: 2021/8/5 13:20 &lt;br&gt;
 * author: ztz &lt;br&gt;
 * version: 1.0 &lt;br&gt;
 */

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {<!-- -->
    public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {<!-- -->
        super(authenticationManager);
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {<!-- -->

        String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER);
        // 如果请求头中没有Authorization信息则直接放行了
        if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) {<!-- -->
            chain.doFilter(request, response);
            return;
        }
        // 如果请求头中有token,则进行解析,并且设置认证信息
        try {<!-- -->
            SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
        } catch (TokenIsExpiredException e) {<!-- -->
            //返回json形式的错误信息
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            String reason = "统一处理,原因:" + e.getMessage();
            response.getWriter().write(new ObjectMapper().writeValueAsString(reason));
            response.getWriter().flush();
            return;
        }
        super.doFilterInternal(request, response, chain);
    }
    // 这里从token中获取用户信息并新建一个token
    private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) throws TokenIsExpiredException {<!-- -->
        String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, "");
        System.out.println(token);
        boolean expiration = JwtTokenUtils.isExpiration(token);
        if (expiration) {<!-- -->
            throw new TokenIsExpiredException("token超时了");
        } else {<!-- -->
            String username = JwtTokenUtils.getUsername(token);
            // redis集成
            //RedisUtil redisUtil = new RedisUtil();
            //String rToken = null;
            //try {<!-- -->
            //    rToken = (String) redisUtil.get(username);
            //} catch (Exception e) {<!-- -->
            //    e.printStackTrace();
            //    return null;
            //}
            //if(!(rToken.equals(tokenHeader))){<!-- -->
            //    return null;
            //}
            String role = JwtTokenUtils.getUserRole(token);
            if (username != null) {<!-- -->
                Collection&lt;GrantedAuthority&gt; authorities = new ArrayList&lt;&gt;();
                UserMapper userMapper = SpringUtil.getBean(UserMapper.class);
                authorities.add(new SimpleGrantedAuthority(role));
                // 数据库中查出所具有的权限进行授权
                for (Permission permission : userMapper.findPermissionByUsername(username)) {<!-- -->
                    authorities.add(new SimpleGrantedAuthority(permission.getPermTag()));
                }
                return new UsernamePasswordAuthenticationToken(username, null,
                       authorities
                );
            }
        }
        return null;
    }

}

正文完