文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

使用SpringSecurity 进行自定义Token校验

2024-04-02 19:55

关注

背景

Spring Security默认使用「用户名/密码」的方式进行登陆校验,并通过cookie的方式存留登陆信息。在一些定制化场景,比如希望单独使用token串进行部分页面的访问权限控制时,默认方案无法支持。

在未能在网上搜索出相关实践的情况下,通过官方文档及个别Stack Overflow的零散案例,形成整体思路并实践测试通过,本文即关于该方案的一个分享。

参考官方文档

SpringSecurity校验流程

基本的SpringSecurity使用方式网上很多,不是本文关注的重点。

关于校验的整个流程简单的说,整个链路有三个关键点,

完整的调用链建议在IDE中通过单步调试亲自体会,本文不做相关整理。

如何自定义

我的需求,是使用自定义的token,验证权限,涉及到:

需要做的事情如下:

TokenAuthentication.java



import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.util.ArrayList;
import java.util.Collection;

public class TokenAuthentication implements Authentication{
    private String token;
    public TokenAuthentication(String token){
        this.token = token;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return new ArrayList<GrantedAuthority>(0);
    }
    @Override
    public Object getCredentials(){
        return token;
    }
    @Override
    public Object getDetails() {
        return null;
    }
    @Override
    public Object getPrincipal() {
        return null;
    }
    @Override
    public boolean isAuthenticated() {
        return true;
    }
    @Override
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
    }
    @Override
    public String getName() {
        return null;
    }
}

AuthenticationTokenFilter.java



import com.google.common.base.Strings;
import com.blaketairan.spring.security.configuration.TokenAuthentication;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Configuration
public class AuthenticationTokenFilter implements Filter{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException{
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain)
            throws IOException, ServletException{
        if (servletRequest instanceof HttpServletRequest){
            String token = ((HttpServletRequest) servletRequest).getHeader("PRIVATE-TOKEN");
            if (!Strings.isNullOrEmpty(token)){
                Authentication authentication = new TokenAuthentication(token);
                SecurityContextHolder.getContext().setAuthentication(authentication);
                System.out.println("Set authentication with non-empty token");
            } else {
                
                Authentication authentication = new TokenAuthentication("");
                SecurityContextHolder.getContext().setAuthentication(authentication);
                System.out.println("Set authentication with empty token");
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
    @Override
    public void destroy(){
    }
}

SecurityPermissionEvalutor.java



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import java.io.Serializable;

public class SecurityPermissionEvaluator implements PermissionEvaluator {
    @Override
    public boolean hasPermission(Authentication authentication,Object targetDomainObject, Object permission){
        String targetDomainObjectString = null;
        String permissionString = null;
        String token = null;
        try {
            targetDomainObjectString = (String)targetDomainObject;
            permissionString = (String)permission;
            token = (String)authentication.getCredentials();
        } catch (ClassCastException e){
            e.printStackTrace();
            return false;
        }
        return hasPermission(token, targetDomainObjectString, permissionString);
    }
    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission){
        
        return false;
    }
    private boolean hasPermission(String token,String targetDomain, String permission){
        
        return true;
    }
}

SecurityConfig.java 全局配置



import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception{
        return super.authenticationManager();
    }
    @Bean
    public PermissionEvaluator permissionEvaluator() {
        
        SecurityPermissionEvaluator securityPermissionEvaluator = new SecurityPermissionEvaluator();
        return securityPermissionEvaluator;
    }
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception{
        
        httpSecurity
                .csrf()
                .disable()
                .httpBasic()
                .disable();
    }
}

BaseRepository.java 某个需要权限验证的方法



import org.springframework.security.access.prepost.PreAuthorize;
import java.util.List;

public interface BaseRepository{
    @PreAuthorize("hasPermission('DOMAIN', 'PERMISSION')")
    void deleteAll();
}

spring security 自定义token无法通过框架认证

自定义token和refreshToken,如代码所示:


UserDO userDO = userMapper.getByName(username);
UserDetails userDetails = 
userService.loadUserByUsername(userForBase.getName());
String token = jwtTokenComponent.generateToken(userDO);
String refreshToken = jwtTokenComponent.generateRefreshToken(userDO);
storeToken(userDO, token,refreshToken);
jsonObject.put("principal", userDetails);
jsonObject.put("token_type", "bearer");
return jsonObject;

无法通过框架的认证?搞它:


UserDO userDO = userMapper.getByName(username);
UserDetails userDetails = 
userService.loadUserByUsername(userForBase.getName());
String token = jwtTokenComponent.generateToken(userDO);
String refreshToken = jwtTokenComponent.generateRefreshToken(userDO);
storeToken(userDO, token,refreshToken);
jsonObject.put("access_token", token);
jsonObject.put("refresh_token", refreshToken);
jsonObject.put("principal", userDetails);
jsonObject.put("token_type", "bearer");
return jsonObject;

private void storeToken(UserDO userDO, String token,String refreshToken) {
        Map<String, String> tokenParams = new HashMap<>();
        tokenParams.put("access_token", token);
        tokenParams.put("expires_in", "7200");
        tokenParams.put("token_type", "bearer");
        OAuth2AccessToken oAuth2AccessToken = DefaultOAuth2AccessToken.valueOf(tokenParams);
        DefaultOAuth2RefreshToken oAuth2RefreshToken = new DefaultOAuth2RefreshToken(refreshToken);
        // 创建redisTemplate,序列化对象
        Map<String, String> requestMap = new HashMap<>();
        requestMap.put("username", userDO.getUsername());
        requestMap.put("grant_type", "password");
        Map<String, Object> queryMap = new HashMap<String, Object>();
        queryMap.put("id",userDO.getUserId());
        List<String> perms = menuMapper.listUserPerms(queryMap);
        Set<GrantedAuthority> authorities = new HashSet<>();
        for (String perm : perms) {
if (StringUtils.isNotBlank(perm)) {
    authorities.add(new SimpleGrantedAuthority(perm.trim()));
}
        }
        OAuth2Request storedRequest = new OAuth2Request(requestMap, "oms-web", authorities, true, null,
    null, null, null, null);
        CustomUserDetails userEnhancer = new CustomUserDetails(userDO, true, true, true, true, authorities);
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userEnhancer, null, userEnhancer.getAuthorities());
        authentication.setDetails(requestMap);
        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(storedRequest, authentication);
        tokenStore.storeAccessToken(oAuth2AccessToken, oAuth2Authentication);
        tokenStore.storeRefreshToken(oAuth2RefreshToken, oAuth2Authentication);
    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯