Spring Boot + Spring Security權限控制

本文在Spring Boot + Spring Security添加記住我功能 的基礎上進行修改html

  • 在登陸的時候,在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,提示以下git

訪問http://localhost:8005/auth/admin 提示以下:github

使用admin密碼123456登陸 提示以下:spring

訪問http://localhost:8005/auth/admin 提示以下:數據庫

注:json

①權限控制註解有:springboot

1.Spring Security自帶的@Secured註解;app

開啓註解:@EnableGlobalMethodSecurity(securedEnabled = true)ide

@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

相關文章
相關標籤/搜索