前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >​springsecurity配合token进行权限控制

​springsecurity配合token进行权限控制

原创
作者头像
ruochen
修改2021-11-20 16:13:33
4500
修改2021-11-20 16:13:33
举报

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依赖

代码语言:txt
复制
&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自定义认证规则,用户的登录就会走这个类去认证,并且在这里进行角色权限的赋予

代码语言:txt
复制
/**
 * 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这个接口并且在配置类中使用这个加密类就好

代码语言:txt
复制
/**
 * 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)核心配置类
代码语言:txt
复制
/**
 * 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的授予
代码语言:txt
复制
/**
 * 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时的验证
代码语言:txt
复制
/**
 * 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;
    }

}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • springsecurity配合token进行权限控制
    • 一、spring security 简介
      • 二、代码
        • 1.项目maven依赖
        • 2.spring-security的配置
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档