使用SpringSecurity 进行自定义Token校验

网友投稿 278 2023-01-04

使用SpringSecurity 进行自定义Token校验

背景

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

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

参考官方文档

SpringSecurity校验流程

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

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

将需要鉴权的类/方法/url),定义为需要鉴权(本文代码示例为方法上注解@PreAuthorize("hasPermission('TARGET','PERMISSION')")

根据访问的信息产生一个来访者的权限信息Authentication,并插入到上下文中

在调用鉴权方法时,根据指定的鉴权方式,验证权限信息是否符合权限要求

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

如何自定义

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

产生Authentication并插入到上下文中

针对token的验证方式

需要做的事情如下:

自定义TokenAuthentication类,实现org.springframework.security.core.Authenticaion,作为token权限信息

自定义AuthenticationTokenFilter类,实现javax.servlet.Filter,在收到访问时,根据访问信息生成TokenAuthentication实例,并插入上下文

自定义SecurityPermissionEvalutor类,实现org.springframework.security.access.PermissionEvaluator,完成权限的自定义验证逻辑

在全局的配置中,定义使用SecurityPermissionEvalutor作为权限校验方式

TokenAuthentication.java

/**

* @author: Blaketairan

*/

import org.springframework.security.core.Authentication;

import org.springframework.security.core.GrantedAuthority;

import java.util.ArrayList;

import java.util.Collection;

/**

* Description: spring-security的Authentication的自定义实现(用于校验token)

*/

public class TokenAuthentication implements Authentication{

private String token;

public TokenAuthentication(String token){

this.token = token;

}

@Override

public Collection extends GrantedAuthority> getAuthorities() {

return new ArrayList(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

/**

* @author: Blaketairan

*/

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;

/**

* Description: 用于处理收到的token并为spring-security上下文生成及注入Authenticaion实例

*/

@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 {

/**

* 在未收到Token时,至少塞入空TokenAuthenticaion实例,避免进入SpringSecurity的用户名密码默认模式

*/

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

/**

* @author: Blaketairan

*/

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.access.PermissionEvaluator;

import org.springframework.security.core.Authentication;

import java.io.Serializable;

/**

* Description: spring-security 自定义的权限处理模块(鉴权)

*/

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){

/**

* 使用@PreAuthorize("hasPermission('TARGET','PERMISSION')")方式,不使用该鉴权逻辑

*/

return false;

}

private boolean hasPermission(String token,String targetDomain, String permission){

/**

* 验证权限

**/

return true;

}

}

SecurityConfig.java 全局配置

/**

* @author: Blaketairan

*/

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;

/**

* Description: spring-security配置,指定使用自定义的权限评估方法

*/

@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{

/**

* 关掉csrf方便本地ip调用调试

**/

httpSecurity

.csrf()

.disable()

.httpBasic()

.disable();

}

}

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

/**

* @author: Blaketairan

*/

import org.springframework.security.access.prepost.PreAuthorize;

import javegUbSXa.util.List;

/**

* Description:

*/

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 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 requestMap = new HashMap<>();

requestMap.put("username", userDO.getUsername());

requestMap.put("grant_type", "password");

Map queryMap = new HashMap();

queryMap.put("id",userDO.getUserId());

List perms = menuMapper.listUserPerms(queryMap);

Set authorities = new HashSet<>();

for (String perm : perms) {

if (StringUtils.isNotBlank(perm)) {

authorities.add(new SimpleGrantedAuthority(perm.trim()));

}

http:// }

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);

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:哪个网站有各种api接口(什么叫api接口)
下一篇:Java设计模式之浅谈模板方法模式
相关文章

 发表评论

暂时没有评论,来抢沙发吧~