资源服务器端通过token解析出来的授权信息的篡改
-
通过自定义tokenService的方式去修改授权信息。
资源服务器接收到请求后,会根据请求中带过来的access_token信息去解析出授权信息,(通过带参数access_token,获取header中添加参数Authorization,值为Bearer+token的方式),此处通过自定义tokenService,重新方法loadAuthentication来修改已授权的信息。
具体实现方式: -
自定义MyTokenServices,重写方法loadAuthentication,方法如下
public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException,
InvalidTokenException {
OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
if (accessToken == null) {
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
}
else if (accessToken.isExpired()) {
tokenStore.removeAccessToken(accessToken);
throw new InvalidTokenException("Access token expired: " + accessTokenValue);
}
OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
try {
//此处为具体修改的地方,其他与源码一致
modify(result,"authorities",AuthorityUtils.commaSeparatedStringToAuthorityList("demo2"));
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (result == null) {
// in case of race condition
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
}
if (clientDetailsService != null) {
String clientId = result.getOAuth2Request().getClientId();
try {
clientDetailsService.loadClientByClientId(clientId);
}
catch (ClientRegistrationException e) {
throw new InvalidTokenException("Client not valid: " + clientId, e);
}
}
return result;
}
-------------------------------------------------
//通过类反射机制,修改权限信息
public static void modify(Object object, String fieldName, Object newFieldValue) throws Exception {
Field field = object.getClass().getSuperclass().getDeclaredField(fieldName);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true); //Field 的 modifiers 是私有的
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
if(!field.isAccessible()) {
field.setAccessible(true);
}
field.set(object, newFieldValue);
}
客户端修改上下文中保存的授权信息
怎么在客户端中修改已从服务器端返回的信息呢,有的时候我们需要在登陆完成后,在单个客户端中自定义一些用户信息或用户权限信息,针对这个场景,需要修改服务端返回的用户授权信息。此处通过自定义过滤器的方式实现。
- 自定义过滤器LoginFilter
@Component
public class LoginFilter extends GenericFilterBean {
private static final String FILTER_APPLIED = "__spring_security_customerFilter_filterApplied";
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
//防止重复调用
if (servletRequest.getAttribute(FILTER_APPLIED) != null) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof OAuth2Authentication) {
OAuth2Authentication originalOAuth2Authentication = (OAuth2Authentication) authentication;
if (!originalOAuth2Authentication.isClientOnly()) {
Authentication userAuthentication = originalOAuth2Authentication.getUserAuthentication();
if (userAuthentication instanceof UsernamePasswordAuthenticationToken) {
UsernamePasswordAuthenticationToken originalUsernamePasswordAuthentication = (UsernamePasswordAuthenticationToken) userAuthentication;
String username = (String) originalUsernamePasswordAuthentication.getPrincipal();
// 可以根据username查找用户,这里模拟获取用户
UsernamePasswordAuthenticationToken usernamePasswordAuthentication = new UsernamePasswordAuthenticationToken(
originalUsernamePasswordAuthentication.getPrincipal(), "N/A",
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
OAuth2Authentication oauth2Authentication = new OAuth2Authentication(
originalOAuth2Authentication.getOAuth2Request(), usernamePasswordAuthentication);
oauth2Authentication.setDetails(originalOAuth2Authentication.getDetails());
SecurityContextHolder.getContext().setAuthentication(oauth2Authentication);
}
}
}
servletRequest.setAttribute(FILTER_APPLIED, true);
filterChain.doFilter(servletRequest, servletResponse);
}
}
- 将自定义的过滤器加入过滤器链当中
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableOAuth2Sso
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${auth-server}")
String authServer;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(getLoginFilter(), SwitchUserFilter.class)
.anonymous().disable()
.authorizeRequests().anyRequest().authenticated().and().csrf().disable().logout().logoutSuccessUrl(authServer+"/exit");
}
@Bean
public LoginFilter getLoginFilter() {
LoginFilter filter = new LoginFilter();
return filter;
}
}
授权服务器修改授权信息
最直接的方式是将客户端设置user_info_uri,通过此方式获取用户信息,在服务器端提供一个资源/user,用来获取用户信息。推荐此种方式,只在登陆的时候调用一次服务端。
- 集成资源服务器
@Configuration
@EnableResourceServer
public class SsoResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable().requestMatchers().antMatchers("/user")
.and()
.authorizeRequests().antMatchers("/user").fullyAuthenticated();
}
}
- 定义/user资源,并且修改授权信息
RequestMapping("/user")
public Principal user(Principal user) {
Authentication authentication = (Authentication) user;
OAuth2Authentication originalOAuth2Authentication = (OAuth2Authentication) authentication;
if (!originalOAuth2Authentication.isClientOnly()) {
Authentication userAuthentication = originalOAuth2Authentication.getUserAuthentication();
String clientId = originalOAuth2Authentication.getOAuth2Request().getClientId();
if (userAuthentication instanceof UsernamePasswordAuthenticationToken) {
UsernamePasswordAuthenticationToken originalUsernamePasswordAuthentication = (UsernamePasswordAuthenticationToken) userAuthentication;
String username = (String) originalUsernamePasswordAuthentication.getPrincipal();
List<String> menuList = userService.getClientUserMenuList(clientId, username);
UsernamePasswordAuthenticationToken usernamePasswordAuthentication = new UsernamePasswordAuthenticationToken(
originalUsernamePasswordAuthentication.getPrincipal(), "N/A",
AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils.join(menuList)));
OAuth2Authentication oauth2Authentication = new OAuth2Authentication(
originalOAuth2Authentication.getOAuth2Request(), usernamePasswordAuthentication);
oauth2Authentication.setDetails(originalOAuth2Authentication.getDetails());
SecurityContextHolder.getContext().setAuthentication(oauth2Authentication);
return oauth2Authentication;
}
}
return authentication;
}