spring security

来源:互联网 发布:js定时器setinterval 编辑:程序博客网 时间:2024/06/05 11:47

本文讲述spring Boot整合Spring Security在方法上使用注解实现权限控制,使用自定义UserDetailService,从MySQL中加载用户信息。使用Security自带的MD5加密,对用户密码进行加密。

spring security 的验证流程:

  1. 用户发出请求
  2. 过滤器拦截(OauthAuthenticationFilter:doFilter)
  3. 取得请求资源所需权限(UserDetailService.loadUserByUsername)
  4. 匹配用户拥有权限和请求权限(AuthenticationProvider authenticate()),如果用户没有相应的权限,
    执行第5步,否则执行第6步。
  5. 登录
  6. 验证并授权(MyUserDetailServiceImpl:loadUserByUsername)

1、配置类 Spring Security

  • 通过 @EnableWebSecurity 注解开启Spring Security的功能
  • 继承 WebSecurityConfigurerAdapter ,并重写它的方法来设置一些web安全的细节
  • configure(HttpSecurity http) 方法
  • 自定义成功/失败的 handle
@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Resource    private FindByIndexNameSessionRepository<ExpiringSession> sessionRepository;    @Resource    private AdminUserDetailsService adminUserDetailsService;    @Resource    private DaoAuthenticationProvider daoAuthenticationProvider;    @Override    protected void configure(HttpSecurity http) throws Exception {        SessionManagementConfigurer<HttpSecurity> sessionManagement = http.sessionManagement();        sessionManagement.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);        sessionManagement.sessionFixation().migrateSession();        http.csrf().disable();        http.authorizeRequests().antMatchers("/*").permitAll()                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll();        http.exceptionHandling().accessDeniedHandler(new JsonAccessDeniedHandler(BaseExceptionEnum.ACCESS_DENIED.getMessage(), BaseExceptionEnum.ACCESS_DENIED.getCode()));        http.logout().logoutUrl("/api/adminUser/logout").logoutSuccessHandler(new JsonLogoutSuccessHandler(BaseExceptionEnum.SUCCESS.getMessage(), BaseExceptionEnum.SUCCESS.getCode()));        http.formLogin().loginPage("/api/adminUser/login").usernameParameter("phone")                .successHandler(new JsonAuthenticationSuccessHandler(BaseExceptionEnum.SUCCESS.getMessage(), BaseExceptionEnum.SUCCESS.getCode()))                .failureHandler(new JsonAuthenticationFailureHandler(BaseExceptionEnum.INVALID_ACCOUNT_PASSWORD.getMessage(), BaseExceptionEnum.INVALID_ACCOUNT_PASSWORD.getCode()));    }    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.authenticationProvider(daoAuthenticationProvider());    }    /**     * 配置数据库校验逻辑:手机号码+密码     *     * @return     */    @Bean    public DaoAuthenticationProvider daoAuthenticationProvider() {        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();        // 设置用户查询处理逻辑的类        daoAuthenticationProvider.setUserDetailsService(adminUserDetailsService);        // 设置密码加密的盐值        // ReflectionSaltSource saltSource = new ReflectionSaltSource();        // saltSource.setUserPropertyToUse("username");        // daoAuthenticationProvider.setSaltSource(saltSource);        // 设置密码加密算法        ShaPasswordEncoder shaPasswordEncoder = new ShaPasswordEncoder(512);        daoAuthenticationProvider.setPasswordEncoder(shaPasswordEncoder);        return daoAuthenticationProvider;    }    @Bean    @Autowired    public SpringSessionBackedSessionRegistry springSessionBackedSessionRegistry(FindByIndexNameSessionRepository sessionRepository) {        return new SpringSessionBackedSessionRegistry(sessionRepository);    }}

2、自定义 UserDetailService

  • 检索使用自己选择的持久化策略的认证信息
  • 在 loadUserByUsername() 获取数据库用户信息,并添加相应的权限到 UserDetails
@Servicepublic class AdminUserDetailsService implements UserDetailsService {    @Resource private AdminUserMapper adminUserMapper;    @Resource private CustomAdminUserMapper customAdminUserMapper;    @Resource private AdminManagementService adminManagementService;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        AdminUserExample example = new AdminUserExample();        example.createCriteria().andPhoneEqualTo(username);        List<AdminUser> list = adminUserMapper.selectByExampleWithBLOBs(example);        if (list == null || list.size() == 0) {            throw new JrdException(JrdExceptionEnum.ADMIN_NOT_FOUND);        }        AdminUser adminUser = list.get(0);        Set<GrantedAuthority> adminAuthorities = adminManagementService.getAdminAuthorities(adminUser);        Boolean accountNonLocked = true;        if (adminUser.getUserStatus() != null)            accountNonLocked = UserStatusEnum.isUserAccountEnabled(adminUser.getUserStatus());        if (adminUser == null) {            throw new UsernameNotFoundException(AdminUserExceptionEnum.ADMIN_USER_NOT_FOUND.getMessage());        }        return new AdminUserDetails(adminUser,adminAuthorities);    }}

3、用声明式注解 @PreAuthorize 修饰 Controller 方法

  这里有两种用法: 
  • @PreAuthorize(“hasAuthority(‘authorized’,’ROLE_XXX)”)
  • @PreAuthorize(“hasPermission(‘authorized’, ”)”)
例如:    @RequestMapping(value = "/saveAccount", method = RequestMethod.POST)    @PreAuthorize("hasPermission('authorized', '')")    public String saveAccount(@Validated(Save.class) User user) {        logger.info(user.getUsername() + " " + user.getCompanyName());        userService.saveUser(user);        return ApiJsonSerializeUtil.jsonResponse(user);    }

第一种 hasAuthority : spring security 会从 UserDetail 里面 getAuthorities() 里拿出该用户对应的权限,然后再自动帮我们校验;

第二种 hasPermission : 需要配置PermissionEvaluator 、GlobalMethodSecurityConfiguration 两个类。在 PermissionEvaluator 中的 hasPrivilege() 方法中进行权限校验。

@Componentpublic class AdminPermissionEvaluator implements PermissionEvaluator {    public static final String AUTHORIZED = "AUTHORIZED";   // 要求登录    @Override    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {        if (authentication == null || !(permission instanceof String)) {            return false;        }        return hasPrivilege(authentication, targetDomainObject.toString(), permission.toString().toUpperCase());    }    @Override    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {        return hasPermission(authentication, null, permission);    }    private Boolean hasPrivilege(Authentication authentication, String targetType, String permission) {        if (authentication instanceof AnonymousAuthenticationToken) {            return false;        }        AdminUserDetails principal = (AdminUserDetails) authentication.getPrincipal();        Set<GrantedAuthority> authorities = (Set<GrantedAuthority>) principal.getAuthorities();        if (!permission.equals("")){            for (GrantedAuthority authoritie : authorities){                if (authoritie.getAuthority().equals(permission)){                    return true;                }            }            return false;        }        if (authentication.getPrincipal() != null && authentication.getPrincipal() instanceof AdminUserDetails) {            return true;        } else {            return false;        }    }}
@Configuration@EnableGlobalMethodSecurity(prePostEnabled = true)public class AdminSecurityMethodConfig extends GlobalMethodSecurityConfiguration {    @Resource private AdminPermissionEvaluator adminPermissionEvaluator;    @Override    protected MethodSecurityExpressionHandler createExpressionHandler() {        DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();        expressionHandler.setPermissionEvaluator(adminPermissionEvaluator);        return expressionHandler;    }}