spring security

本文讲述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
@EnableWebSecurity
public 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
@Service
public 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() 方法中进行权限校验。

@Component
public 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;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值