springsecurity配合token进行权限控制

springsecurity配合token进行权限控制

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

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

一、spring security 简介

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

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

原理图

springsecurity配合token进行权限控制_第1张图片

二、代码

1.项目maven依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jdbcartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-configuration-processorartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-securityartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.10version>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.21version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-testartifactId>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.4.1version>
        dependency>
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-generatorartifactId>
            <version>3.4.1version>
        dependency>
        
        <dependency>
            <groupId>org.apache.velocitygroupId>
            <artifactId>velocity-engine-coreartifactId>
            <version>2.0version>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
        dependency>
        <dependency>
            <groupId>ma.glasnost.orikagroupId>
            <artifactId>orika-coreartifactId>
            <version>1.5.2version>
        dependency>
        
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger2artifactId>
            <version>2.9.2version>
        dependency>
        
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger-uiartifactId>
            <version>2.9.2version>
        dependency>
        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
            <version>0.9.1version>
        dependency>
        <dependency>
            <groupId>org.codehaus.jacksongroupId>
            <artifactId>jackson-mapper-aslartifactId>
            <version>1.9.13version>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.75version>
        dependency>
    dependencies>

2.spring-security的配置

(1)认证类

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

/**
 * description: 用户认证 
* date: 2021/8/1 15:14
* author: ztz
* version: 1.0
*/
@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<User>().eq("user_name", username)); if(user==null){ return null; }else { Collection<GrantedAuthority> authorities = new ArrayList<>(); // 数据库中查询出所拥有的角色进行赋予 for (Role role : userMapper.findRoleByUsername(username)) { authorities.add(new SimpleGrantedAuthority(role.getRoleName())); } return new JwtUser(user,authorities); } } }

(2)自定义加密规则

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

/**
 * description: 自定义加密规则  
* date: 2021/8/1 18:22
* author: ztz
* version: 1.0
* @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配置 
* date: 2021/7/31 18:15
* author: ztz
* version: 1.0
*/
@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的授予 
* date: 2021/8/5 13:19
* author: ztz
* version: 1.0
*/
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private ThreadLocal<Integer> rememberMe = new ThreadLocal<>(); 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<>()) ); } 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<? extends GrantedAuthority> 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时的验证  
* date: 2021/8/5 13:20
* author: ztz
* version: 1.0
*/
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<GrantedAuthority> authorities = new ArrayList<>(); 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; } }

你可能感兴趣的:(java,java)