springsecurity权限控制_1.spring security基于rbac权限控制

  1. 执行流程图

c43f6b13b26a9768c4e81c7795d9b30b.png

2. 引入依赖

<dependency>            <groupId>org.projectlombokgroupId>            <artifactId>lombokartifactId>        dependency>        <dependency>            <groupId>org.springframework.cloudgroupId>            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>        dependency>        <dependency>            <groupId>org.springframework.bootgroupId>            <artifactId>spring-boot-starter-securityartifactId>        dependency>        <dependency>            <groupId>org.apache.commonsgroupId>            <artifactId>commons-lang3artifactId>        dependency>        <dependency>            <groupId>com.maydaygroupId>            <artifactId>mayday_commonartifactId>            <version>1.0-SNAPSHOTversion>        dependency>        <dependency>            <groupId>mysqlgroupId>            <artifactId>mysql-connector-javaartifactId>        dependency>        <dependency>            <groupId>tk.mybatisgroupId>            <artifactId>mapper-spring-boot-starterartifactId>            <version>2.1.5version>        dependency>        <dependency>            <groupId>org.springframework.bootgroupId>            <artifactId>spring-boot-starter-data-redisartifactId>        dependency>

3. 配置文件

server:  port: 9003spring:  application:    name: mayday-auth  datasource:    driverClassName: com.mysql.jdbc.Driver    url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf8&useSSL=false    username: root    password: admin  redis:    host: 127.0.0.1eureka:  client:    serviceUrl:      defaultZone: http://127.0.0.1:9001/eureka/  instance:    prefer-ip-address: truemybatis:  type-aliases-package: com.mayday.auth.pojo  configuration:    map-underscore-to-camel-case: truersa:  key:    pubKeyFile: D:/document/key/key_rsa.pub    priKeyFile: D:/document/key/key_rsa  allowPaths:    - /auth/login/**    - /auth/code/**

4. 创建pojo类

@Data@Entity@Table(name = "tb_user")public class User implements UserDetails {    @Id    @KeySql(useGeneratedKeys = true)    private Long id;    private String username;    private String password;    private Boolean status;    private String phone;    @Transient    private String checkCode;    private List roles;    @Override    @JsonIgnore    public Collection extends GrantedAuthority> getAuthorities() {        return roles.stream().map(Role::getPermissions).findAny().get();    }    @Override    @JsonIgnore    public boolean isAccountNonExpired() {        return true;    }    @Override    @JsonIgnore    public boolean isAccountNonLocked() {        return true;    }    @Override    @JsonIgnore    public boolean isCredentialsNonExpired() {        return true;    }    @Override    @JsonIgnore    public boolean isEnabled() {        return true;    }}
@Data@Entity@Table(name = "tb_role")public class Role {    @Id    @KeySql(useGeneratedKeys = true)    private Long id;    @Column(name = "role_name")    private String roleName;    @Column(name = "role_desc")    private String roleDesc;    private List permissions;}
@Data@Entity@Table(name = "tb_permission")public class Permission implements GrantedAuthority {    @Id    @KeySql(useGeneratedKeys = true)    private Long id;    @Column(name = "permission_name")    private String permissionName;    @Column(name = "permission_desc")    private String permissionDesc;    @Override    @JsonIgnore    public String getAuthority() {        return permissionName;    }}

5.创建dao和service层

user接口public interface UserMapper extends Mapper<User> {}role接口public interface RoleMapper extends Mapper<Role> {    @Select("select r.id,r.role_name,r.role_desc from tb_role r left join tb_user_role ur on  r.id = ur.role_id where ur.user_id = #{userId}")    ListselectAllByUserId(Long userId);}permission接口public interface PermissionMapper extends Mapper<Permission> {    @Select("select p.id,p.permission_name,p.permission_desc from tb_permission p left join tb_role_permission rp on  p.id = rp.permission_id where rp.role_id = #{roleId}")    ListselectAllByRoleId(Long roleId);}
@Servicepublic class UserService implements UserDetailsService {    @Autowired    private UserMapper userMapper;    @Autowired    private PermissionMapper permissionMapper;    @Autowired    private RoleMapper roleMapper;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        User user = new User();        user.setUsername(username);        User currentUser = userMapper.selectOne(user);        if (currentUser == null) {            return null;        }        //设置用户的角色        List roles = roleMapper.selectAllByUserId(currentUser.getId());        if (!CollectionUtils.isEmpty(roles)) {            for (Role role : roles) {                //查询每个角色对应的权限                List permissions = permissionMapper.selectAllByRoleId(role.getId());                if (!CollectionUtils.isEmpty(permissions)) {                    role.setPermissions(permissions);                }            }            currentUser.setRoles(roles);        }        return currentUser;    }}

6. 创建Controller层

@RestController@RequestMapping("/auth")public class UserController {    @Autowired    private UserService userService;    @Autowired    private StringRedisTemplate redisTemplate;    @Autowired    private RsaKeyProperties rsaKeyProperties;    private static final String REDIS_PREFIX = "mayday:login:code";    private static final int OUT_TIME = 30;    private static final String TOKEN_PREFIX = "Bearer ";    /**     * 生成验证码     *     * @param response     * @param request     * @throws IOException     */    @GetMapping("/code")    public void captcha(HttpServletResponse response, HttpServletRequest request) throws IOException {        response.setHeader("Cache-Control", "no-store, no-cache");        response.setContentType("image/jpeg");        // 生成图片验证码        BufferedImage image = CaptchaUtil.createImage();        // 生成文字验证码        String randomText = CaptchaUtil.drawRandomText(image);        // 保存到验证码到 redis 有效期两分钟        redisTemplate.opsForValue().set(REDIS_PREFIX, randomText.toLowerCase(), 1, TimeUnit.MINUTES);        ServletOutputStream out = response.getOutputStream();        ImageIO.write(image, "jpeg", out);    }    /**     * 登录     *     * @param user     * @return     */    @PostMapping("/login")    public Result login(@RequestBody User user) {        //检查验证码是否正确        String checkCode = user.getCheckCode();        String redisCode = redisTemplate.opsForValue().get(REDIS_PREFIX);        if (StringUtils.isEmpty(checkCode) || !redisCode.equals(checkCode)) {            return new Result(false, StatusCode.CHECK_CODE_NOT_EXIT.getCode(), StatusCode.CHECK_CODE_NOT_EXIT.getMsg());        }        //验证登录        User currentUser = (User) userService.loadUserByUsername(user.getUsername());        if (currentUser == null) {            return new Result(false, StatusCode.USERNAME_NOT_FOUND.getCode(), StatusCode.USERNAME_NOT_FOUND.getMsg());        }        //检验密码是否输入正确        if (new BCryptPasswordEncoder().matches(user.getPassword(), currentUser.getPassword())) {            return new Result(false, StatusCode.LOGIN_ERROR.getCode(), StatusCode.LOGIN_ERROR.getMsg());        }        String token = TOKEN_PREFIX + JwtUtils.generateTokenExploreInMinutes(currentUser, rsaKeyProperties.getPrivateKey(), OUT_TIME);        return new Result(true, StatusCode.OK.getCode(), StatusCode.OK.getMsg(), token);    }    /**     * 权限测试接口     * @return     */    @GetMapping("test")    @PreAuthorize("hasAnyAuthority('product_list')")    public String test() {        return "test success";    }}

7. 创建配置类

@Data@ConfigurationProperties("rsa")public class AllowPathsProperties {    private List allowPaths;}@Data@ConfigurationProperties("rsa")public class AllowPathsProperties {    private List allowPaths;}

8. 创建Spring Security配置类

@Slf4j@Configuration@EnableGlobalMethodSecurity(prePostEnabled = true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private UserService userService;    @Autowired    private RsaKeyProperties rsaKeyProperties;    @Autowired    private AllowPathsProperties allowPathsProperties;    /**     * 解决无法直接注入AuthenticationManager     *     * @return     * @throws Exception     */    @Bean    @Override    public AuthenticationManager authenticationManagerBean() throws Exception {        return super.authenticationManagerBean();    }    /**     * 装载BCrypt密码编码器 密码加密     *     * @return     */    @Bean    public BCryptPasswordEncoder passwordEncoder() {        return new BCryptPasswordEncoder();    }    @Override    public void configure(AuthenticationManagerBuilder auth) throws Exception {        // 设置UserDetailsService   使用BCrypt进行密码的hash        auth.userDetailsService(userService).passwordEncoder(this.passwordEncoder());    }    @Override    public void configure(HttpSecurity http) throws Exception {        http                // 由于使用的是JWT,我们这里不需要csrf                .csrf().disable()                // 过滤请求                .authorizeRequests()                // 对于登录login 图标 要允许匿名访问                .antMatchers(allowPathsProperties.getAllowPaths().stream().toArray(String[]::new)).anonymous()                // 除上面外的所有请求全部需要鉴权认证                .anyRequest().authenticated()                .and()                .headers().frameOptions().disable()                .and().addFilter(new LoginFilter(super.authenticationManager(), rsaKeyProperties))                //基于token,不需要session                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)        ;    }}

9.创建过滤器类

public class LoginFilter extends BasicAuthenticationFilter {    private RsaKeyProperties rsaKeyProperties;    public LoginFilter(AuthenticationManager authenticationManager, RsaKeyProperties rsaKeyProperties) {        super(authenticationManager);        this.rsaKeyProperties = rsaKeyProperties;    }    @Override    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {        String header = request.getHeader("Authorization");        if (header == null || !header.startsWith("Bearer ")) {            //如果携带错误的token,则给用户提示请登录!            chain.doFilter(request, response);            response.setContentType("application/json;charset=utf-8");            response.setStatus(HttpServletResponse.SC_FORBIDDEN);            PrintWriter out = response.getWriter();            Map resultMap = new HashMap();            resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);            resultMap.put("msg", "请登录!");            out.write(new ObjectMapper().writeValueAsString(resultMap));            out.flush();            out.close();        } else {            //如果携带了正确格式的token要先得到token            String token = header.replace("Bearer ", "");            //验证token是否正确            Payload payload = JwtUtils.genInfoFromToken(token, rsaKeyProperties.getPublicKey(), User.class);            User user = payload.getUserInfo();            if (user != null) {                UsernamePasswordAuthenticationToken authResult = new UsernamePasswordAuthenticationToken(user.getUsername(), null, user.getAuthorities());                SecurityContextHolder.getContext().setAuthentication(authResult);                chain.doFilter(request, response);            }        }    }}

10. 创建自定义异常处理类

/** * 认证失败处理类 返回401 * * @author lihaodong */@Slf4j@Componentpublic class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {    @Override    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);        //如果携带错误的token,则给用户提示请登录!        PrintWriter out = response.getWriter();        Result result = new Result(false, StatusCode.UN_AUTHORIZATION);        out.write(new ObjectMapper().writeValueAsString(result));        out.flush();        out.close();    }}/** * 权限不足认证 */@Componentpublic class AccessDeniedHandlerImpl implements AccessDeniedHandler {    @Override    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);        //如果携带错误的token,则给用户提示请登录!        PrintWriter out = httpServletResponse.getWriter();        Result result = new Result(false, StatusCode.ACCESS_ERROR);        out.write(new ObjectMapper().writeValueAsString(result));        out.flush();        out.close();    }} @Autowired    private AuthenticationEntryPointImpl authenticationEntryPoint;    @Autowired    private AccessDeniedHandlerImpl accessDeniedHandler;     @Override    public void configure(HttpSecurity http) throws Exception {        http                // 由于使用的是JWT,我们这里不需要csrf                .csrf().disable()                //认证失败处理类                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler)                }}

11.测试结果

daf08f33eb2619d189167306f26f3024.png

162e42321efcefb599cbb698e215babb.png

049cc5567fdd90ba254d65ab53c5c572.png

118cd9726d164ea8892d1e03e2fa94f0.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值