记录下Eladmin源码中学习Spring Security使用的一些小笔记

security注解放行思路:

* 创建注解,在指定controller上标注
* 在WebSecurityConfigurerAdapter的继承子类上进行配置放行

1.创建注解,在指定controller上标注

/**
* @author jacky
*  用于标记匿名访问方法
*/
@Inherited
@Documented
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonymousAccess {
}

/**
* Annotation for mapping HTTP {@code PATCH} requests onto specific handler
* methods.
* * 支持匿名访问    PatchMapping
*
* @author liaojinlong
* @see AnonymousGetMapping
* @see AnonymousPostMapping
* @see AnonymousPutMapping
* @see AnonymousDeleteMapping
* @see RequestMapping
*/
@AnonymousAccess
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.PATCH)
public @interface AnonymousPatchMapping {

    /**
     * Alias for {@link RequestMapping#name}.
     */
    @AliasFor(annotation = RequestMapping.class)
    String name() default "";

    /**
     * Alias for {@link RequestMapping#value}.
     */
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};

    /**
     * Alias for {@link RequestMapping#path}.
     */
    @AliasFor(annotation = RequestMapping.class)
    String[] path() default {};


    /**
     * Alias for {@link RequestMapping#params}.
     */
    @AliasFor(annotation = RequestMapping.class)
    String[] params() default {};


    /**
     * Alias for {@link RequestMapping#headers}.
     */
    @AliasFor(annotation = RequestMapping.class)
    String[] headers() default {};

    /**
     * Alias for {@link RequestMapping#consumes}.
     */
    @AliasFor(annotation = RequestMapping.class)
    String[] consumes() default {};

    /**
     * Alias for {@link RequestMapping#produces}.
     */
    @AliasFor(annotation = RequestMapping.class)
    String[] produces() default {};
}

在指定controller中rest方法上加该注解,进行标注

@AnonymousPostMapping(value = "/login")

2.在WebSecurityConfigurerAdapter的继承子类SpringSecurityConfig上进行配置放行


// 搜寻匿名标记 url: @AnonymousAccess
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
// 获取匿名标记
Map<String, Set<String>> anonymousUrls = getAnonymousUrl(handlerMethodMap);
httpSecurity
        // 禁用 CSRF
        .csrf().disable()
        .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
        // 授权异常
        .exceptionHandling()
        .authenticationEntryPoint(authenticationErrorHandler)
        .accessDeniedHandler(jwtAccessDeniedHandler)
        // 防止iframe 造成跨域
        .and()
        .headers()
        .frameOptions()
        .disable()
        // 不创建会话
        .and()
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
        // 静态资源等等
        .antMatchers(
                HttpMethod.GET,
                "/*.html",
                "/**/*.html",
                "/**/*.css",
                "/**/*.js",
                "/webSocket/**"
        ).permitAll()
        // swagger 文档
        .antMatchers("/swagger-ui.html").permitAll()
        .antMatchers("/swagger-resources/**").permitAll()
        .antMatchers("/webjars/**").permitAll()
        .antMatchers("/*/api-docs").permitAll()
        // 文件
        .antMatchers("/avatar/**").permitAll()
        .antMatchers("/file/**").permitAll()
        // 阿里巴巴 druid
        .antMatchers("/druid/**").permitAll()
        // 放行OPTIONS请求
        .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
        // 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型
        // GET
        .antMatchers(HttpMethod.GET, anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll()
        // POST
        .antMatchers(HttpMethod.POST, anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll()
        // PUT
        .antMatchers(HttpMethod.PUT, anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll()
        // PATCH
        .antMatchers(HttpMethod.PATCH, anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll()
        // DELETE
        .antMatchers(HttpMethod.DELETE, anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll()
        // 所有类型的接口都放行
        .antMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll()
        // 所有请求都需要认证
        .anyRequest().authenticated()
        .and().apply(securityConfigurerAdapter());



private Map<String, Set<String>> getAnonymousUrl(Map<RequestMappingInfo, HandlerMethod> handlerMethodMap) {
    Map<String, Set<String>> anonymousUrls = new HashMap<>(8);
    Set<String> get = new HashSet<>();
    Set<String> post = new HashSet<>();
    Set<String> put = new HashSet<>();
    Set<String> patch = new HashSet<>();
    Set<String> delete = new HashSet<>();
    Set<String> all = new HashSet<>();
    for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
        HandlerMethod handlerMethod = infoEntry.getValue();
        AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
        if (null != anonymousAccess) {
            List<RequestMethod> requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());
            RequestMethodEnum request = RequestMethodEnum.find(requestMethods.size() == 0 ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name());
            switch (Objects.requireNonNull(request)) {
                case GET:
                    get.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
                    break;
                case POST:
                    post.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
                    break;
                case PUT:
                    put.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
                    break;
                case PATCH:
                    patch.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
                    break;
                case DELETE:
                    delete.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
                    break;
                default:
                    all.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
                    break;
            }
        }
    }
    anonymousUrls.put(RequestMethodEnum.GET.getType(), get);
    anonymousUrls.put(RequestMethodEnum.POST.getType(), post);
    anonymousUrls.put(RequestMethodEnum.PUT.getType(), put);
    anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch);
    anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete);
    anonymousUrls.put(RequestMethodEnum.ALL.getType(), all);
    return anonymousUrls;
}

security注解权限思路:

* security提供指定权限注解@PreAuthorize("@el.check()")值可以是方法@PreAuthorize("hasAnyRole('admin','menu:edit')") 方法返回boolean值即可
* 创建el类和方法check()

1.创建el类和方法check()

@Service(value = "el")
public class ElPermissionConfig {
    public Boolean check(String ...permissions){
        // 获取当前用户的所有权限
        List<String> elPermissions = SecurityUtils.getCurrentUser().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
        // 判断当前用户的所有权限是否包含接口上定义的权限
        return elPermissions.contains("admin") || Arrays.stream(permissions).anyMatch(elPermissions::contains);
    }
}

security获取身份域中信息相关类
核心代码: SecurityContextHolder.getContext().getAuthentication();


public class SecurityUtils {


    /**
     * 获取当前登录的用户
     * @return UserDetails
     */
    public static UserDetails getCurrentUser() {
        UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class);
        return userDetailsService.loadUserByUsername(getCurrentUsername());
    }

/**
* 获取系统用户名称
*
* @return 系统用户名称
*/
public static String getCurrentUsername() {
    final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null) {
        throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期");
    }
    if (authentication.getPrincipal() instanceof UserDetails) {
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        return userDetails.getUsername();
    }
    throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息");
}
}

Security在controller设置提前验证身份:

1.(核心代码)

//设置登录的账户密码,并调取Security验证(调取-->UserDetailsService的自定义实现类的loadUserByUsername 查询数据库的用户-->比较用户对比,正确则返回验证类,错误则报错)
UsernamePasswordAuthenticationToken authenticationToken =
        new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);

2.login实际运用

@Log("用户登录")
@ApiOperation("登录授权")
@AnonymousPostMapping(value = "/login")
public ResponseEntity<Object> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
    // 密码解密
    String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
    // 查询验证码
    String code = (String) redisUtils.get(authUser.getUuid());
    // 清除验证码
    redisUtils.del(authUser.getUuid());
    if (StringUtils.isBlank(code)) {
        throw new BadRequestException("验证码不存在或已过期");
    }
    if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
        throw new BadRequestException("验证码错误");
    }
    UsernamePasswordAuthenticationToken authenticationToken =
            new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
    Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
    SecurityContextHolder.getContext().setAuthentication(authentication);
    // 生成令牌与第三方系统获取令牌方式
    // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername());
    // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    // SecurityContextHolder.getContext().setAuthentication(authentication);
    String token = tokenProvider.createToken(authentication);
    final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
    // 保存在线信息
    onlineUserService.save(jwtUserDto, token, request);
    // 返回 token 与 用户信息
    Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
        put("token", properties.getTokenStartWith() + token);
        put("user", jwtUserDto);
    }};
    if (loginProperties.isSingleLogin()) {
        //踢掉之前已经登录的token
        onlineUserService.checkLoginOnUser(authUser.getUsername(), token);
    }
    return ResponseEntity.ok(authInfo);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值