Spring Boot + Spring Security权限控制

点点圈 提交于 2020-08-08 12:26:53

本文在Spring Boot + Spring Security添加记住我功能 的基础上进行修改

  • 在登录的时候,在UserDetailService中认证并授权,修改UserDetailService
@Configuration
public class UserDetailService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 模拟一个用户,替代数据库获取逻辑
        MyUser user = new MyUser();
        user.setUserName(username);
        user.setPassword(this.passwordEncoder.encode("123456"));
        // 输出加密后的密码
        System.out.println(user.getPassword());

        //授权  真正的应用中会根据数据中的查询得知
        List<GrantedAuthority> authorities = new ArrayList<>();
        if (StringUtils.equalsIgnoreCase("admin", username)) {
            authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
        } else {
            authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("test");
        }
        return new User(username, user.getPassword(), user.isEnabled(),
                user.isAccountNonExpired(), user.isCredentialsNonExpired(),
                user.isAccountNonLocked(), authorities);
    }
}
  • 新建自定义权限辅助类:
@Component
public class MyAuthenticationAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write("很抱歉,您没有该访问权限");
    }
}
  • 开启权限注解并配置权限辅助类:

@Component
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启权限注解
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyAuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private MyAuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    private ValidateCodeFilter validateCodeFilter;
    @Autowired
    private UserDetailService userDetailService;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private MyAuthenticationAccessDeniedHandler myAuthenticationAccessDeniedHandler;

    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(false);
        return jdbcTokenRepository;
    }
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
         http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器
                 .exceptionHandling()
                 .accessDeniedHandler(myAuthenticationAccessDeniedHandler) // 配置辅助类
                 .and()
                 .formLogin() // 表单登录
                // http.httpBasic() // HTTP Basic
                .loginPage("/authentication/require") // 登录跳转 URL
                .loginProcessingUrl("/login") // 处理表单登录 URL
                .failureHandler(authenticationFailureHandler) // 处理登录失败
                .successHandler(authenticationSuccessHandler)
                 .and()
                 .rememberMe() // 启用rememberMe
                 .tokenRepository(persistentTokenRepository()) // 配置 token 持久化仓库
                 .tokenValiditySeconds(3600) // remember 过期时间,单为秒
                 .userDetailsService(userDetailService) // 处理自动登录逻辑
                .and()
                .authorizeRequests() // 授权配置
                .antMatchers("/authentication/require",
                        "/login.html",
                        "/code/image").permitAll() // 无需认证的请求路径
                .anyRequest()  // 所有请求
                .authenticated() // 都需要认证
                .and().csrf().disable();
    }
}
  • 修改IndexController:
@RestController
public class IndexController {
    @GetMapping("index")
    public Object index(){
        return SecurityContextHolder.getContext().getAuthentication();
    }
    @GetMapping("/auth/admin")
    @PreAuthorize("hasAuthority('admin')")   // 权限控制注解①
    public String authenticationTest() {
        return "您拥有admin权限,可以查看";
    }
}
  • 测试:

启动项目:访问http://localhost:8005/login.html ,填写用户名user密码123456,提示如下

访问http://localhost:8005/auth/admin 提示如下:

使用admin密码123456登录 提示如下:

访问http://localhost:8005/auth/admin 提示如下:

注:

①权限控制注解有:

1.Spring Security自带的@Secured注解;

开启注解:@EnableGlobalMethodSecurity(securedEnabled = true)

@Secured("ROLE_ADMIN")  // 或者({"ROLE_ADMIN","ROLE_USER"})
public void test(){
    ...
}

2.JSR-250的@RolesAllowed注解;

@EnableGlobalMethodSecurity(jsr250Enabled = true)

@RolesAllowed("ROLE_ADMIN")
public void test(){
    ...
}

3.表达式驱动的注解,包括@PreAuthorize、@PostAuthorize、@PreFilter和 @PostFilter

开启注解: @EnableGlobalMethodSecurity(prePostEnabled = true)

//该注解用于方法前验证权限,下方是:限制非VIP用户提交blog的note字段字数不得超过1000字
//#form部分直接引用了方法中的同名参数。这使得Spring Security能够检查传入方法的参数,并将这些参数用于认证决策的制定
@PreAuthorize("hasRole('ROLE_ADMIN') and #form.note.length() <= 1000 or hasRole('ROLE_VIP')")
public void writeBlog(Form form){
    ...
}
或者:
//方法后调用权限验证,校验方法返回值 用户名是否正确
//Spring Security在SpEL中提供了名为returnObject 的变量。在这里方法返回一个User对象,所以这个表达式可以直接访问user对象中的userName属性。
@PreAuthorize("hasRole(ROLE_USER)")
@PostAuthorize("returnObject.user.userName == principal.username")
public User getUserById(long id){
    ...

本文代码运行正常

本文源代码:https://github.com/ttdys/springboot/tree/master/springboot_security/05_role_permission

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!