您现在的位置是:首页 > 正文

SpringSecurity实现Oauth2.0

2024-04-01 00:56:23阅读 4
  1. 使用SpringSecurity实现Oauth2.0服务端,单独的认证、授权服务,认证用来校验账户是否是合法,授权用来给与当前持有token的角色。
    服务端定义AuthorizationServerConfigurerAdapter类子类,WebSecurityConfigurerAdapter类子类,AuthenticationProvider类子类,实现UserDetailsService接口。
import com.minmaxtec.oauth.service.ecus.EcusUserService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;

import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

@Log4j2
@Configuration
@EnableAuthorizationServer
public class ServerAuthorizationConfig extends AuthorizationServerConfigurerAdapter {

	@Autowired
	public RedisConnectionFactory redisConnectionFactory;
	@Autowired
	@Qualifier("authenticationManagerBean")
	private AuthenticationManager authenticationManager;

	private TokenEndpoint tokenEndpoint;

//	@Autowired
//	private UserService userService;

	@Autowired
	private EcusUserService ecusUserService;

	/**
	 * 针对端点的配置
	 * password模式需要提供一个AuthenticationManager到AuthorizationServerEndpointConfigurer
	 *
	 * @param authorizationServerEndpointsConfigurer
	 * @throws Exception
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer) throws Exception {
		authorizationServerEndpointsConfigurer.tokenStore(tokenStore())
			.authenticationManager(authenticationManager)
			.userDetailsService(ecusUserService).tokenServices(tokenService());
		authorizationServerEndpointsConfigurer.exceptionTranslator(new AuthExceptionTranslator())
			.allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET)
		.pathMapping("/oauth/token", "/oauth/fgt/token");
	}

	/**
	 * @param clientDetailsServiceConfigurer
	 * @throws Exception
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {
		clientDetailsServiceConfigurer.inMemory().withClient("client")
			.accessTokenValiditySeconds(60 * 60 * 3)
			.refreshTokenValiditySeconds(60 * 60 * 3)
			.secret(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("secret"))
			.redirectUris("重定向地址")
			.scopes("all")
//				.authorities("COREQI_READ")
			.authorizedGrantTypes("authorization_code", "implicit", "password", "refresh_token").autoApprove(true);
	}


	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		//isAuthenticated()
		security.tokenKeyAccess("permitAll()").checkTokenAccess("permitAll()").allowFormAuthenticationForClients();
	}

	@Bean("redisTokenStore")
	@Primary
	public TokenStore tokenStore() {
		RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
		redisTokenStore.setPrefix("auth-token:");
		return redisTokenStore;
	}

	@Bean
	@Primary
	public DefaultTokenServices tokenService() {
		DefaultTokenServices tokenServices = new DefaultTokenServices();
		//配置token存储
		tokenServices.setTokenStore(tokenStore());
		//开启支持refresh_token,此处如果之前没有配置,启动服务后再配置重启服务,可能会导致不返回token的问题,解决方式:清除redis对应token存储
		tokenServices.setSupportRefreshToken(true);
		//复用refresh_token
		tokenServices.setReuseRefreshToken(false);
		//token有效期
		tokenServices.setAccessTokenValiditySeconds(3 * 60 * 60);
		//refresh_token有效期
		tokenServices.setRefreshTokenValiditySeconds(3 * 60 * 60);
//		tokenServices.setTokenEnhancer(new TokenEnhancer() {
//
//			@Override
//			public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
//				Map<String, Object> info = new HashMap<>();
//				User user = (User) oAuth2Authentication.getUserAuthentication().getPrincipal();
//				info.put("userid", user.getUsername());
//				info.put("role", ArrayUtils.toArray(user.getAuthorities()));
//				((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info);
//				return oAuth2AccessToken;
//			}
//		});
		return tokenServices;
	}


}

import com.minmaxtec.oauth.service.core.UserAuthenticationProvider;
import com.minmaxtec.oauth.service.ecus.EcusUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


@Configuration
@ComponentScan("包路径")
@EnableWebSecurity
public class ServerWebSecurityConfig extends WebSecurityConfigurerAdapter {
//	@Autowired
//	private UserService userService;
	@Autowired
	private EcusUserService ecusUserService;
	@Autowired
	private UserAuthenticationProvider userAuthenticationProvider;

	@Override
	@Bean("authenticationManagerBean")
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
	}

	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {
//		formLogin().loginPage("login").and().
		httpSecurity.formLogin().and().httpBasic().and()
			.authorizeRequests().antMatchers("/oauth/*","/oauth/verifycode","/sys/**/*")
			.permitAll()
			.anyRequest().authenticated()//任何请求都需要身份认证
			.and()
			.logout()
			.deleteCookies("remove")
			.invalidateHttpSession(false)
			.logoutUrl("/logout")
			.permitAll()
			.and()
			.cors().disable()
			.csrf().disable();//禁用CSRF
	}

	@Override
	protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
//		authenticationManagerBuilder.authenticationProvider(daoAuthenticationProvider());
		authenticationManagerBuilder.userDetailsService(ecusUserService).and().authenticationProvider(userAuthenticationProvider);
	}

//	@Bean
//	public DaoAuthenticationProvider daoAuthenticationProvider(){
//		DaoAuthenticationProvider daoAuthenticationProvider=new DaoAuthenticationProvider();
//		daoAuthenticationProvider.setUserDetailsService(userService);
//		//禁止隐藏用户未找到异常
//		daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
//		//使用BCrypt进行密码的hash
//		daoAuthenticationProvider.setPasswordEncoder(UserService.passwordEncoder());
//		return daoAuthenticationProvider;
//
//	}


}

import cn.hutool.core.lang.Validator;

import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.util.ArrayList;
import java.util.List;


@Log4j2
@Service
public class UserService implements UserDetailsService {

	@Autowired
	private UserDetailMapper userDetailMapper;
	@Autowired
	private AccountMapper accountMapper;
	@Autowired
	private RedisTemplate<String, String> redisTemplate;
	@Autowired
	private SendMobileMsg sendMobileMsg;
    @Override
    public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
	    EcusUser ecusUser=null;
	    if(Validator.isMobile(userId)){
			ecusUser=userDetailMapper.queryUserDetailByMobile(userId);
	    }else {
		    ecusUser=userDetailMapper.queryUserDetailByUserId(userId);
	    }
	    if(ecusUser==null){
		    throw OAuth2Exception.create(OAuth2Exception.ACCESS_DENIED,"此账号不存在!");
	    }
	    if(StringUtils.isEmpty(ecusUser.getPassword())){
		    throw OAuth2Exception.create(OAuth2Exception.ACCESS_DENIED,OAuth2Exception.ACCESS_DENIED);
	    }
	    if(StringUtils.isEmpty(ecusUser.getRole())){
	    	ecusUser.setRole("admin");
	    }
	    UserDetails userDetails=new User(ecusUser.getUserId(),ecusUser.getPassword(),getAuthorities(ecusUser.getRole()));
	    return userDetails;
    }

	private List<GrantedAuthority> getAuthorities(String role){
    	List<String> roles=new ArrayList<>();
    	roles.add(role);
		List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
		roles.forEach(roleCode->{
			authList.add(new SimpleGrantedAuthority(roleCode));
		});
		return authList;
	}

	public void bindEmail(String email, String validCode, String userId) throws Exception{
		String msgCode = redisTemplate.opsForValue().get("eMail:"+email+":"+userId);
		if (StringUtils.isEmpty(msgCode)||!StringUtils.equals(validCode,msgCode)) {
			throw new Exception("验证码错误");
		}
		ecusAccountMapper.updateEmailByUserid(userId, email);
		redisTemplate.delete("eMail:" + email + ":" + userId);
	}

	public void bindMobilePhone(String mobilePhone, String validCode, String userId) throws Exception{
    	if (sendMobileMsg.checkMobileCode(mobilePhone, validCode)) {
    		ecusAccountMapper.updatePhoneByUserid(userId, mobilePhone);
    	} else {
    		throw new Exception("验证码错误");
    	}

	}


	public void modifyPasswd(String oldPasswd, String newPasswd, String confNewPasswd,String userId) throws Exception {
		if (! confNewPasswd.equals(newPasswd)){
			throw new Exception("确认新密码与新密码不一致");
		}
		String oldpwd = md5PasswordEncoder(oldPasswd).substring(8,24);
		String curentPwd = ecusAccountMapper.getUserPasswd(userId);
		if (!oldpwd.equals(curentPwd)&&!oldpwd.substring(8,24).equals(curentPwd)){
			throw new Exception("旧密码不正确");
		}
		updatePwd(newPasswd,userId);
	}

	public int updatePwd(String password,String userId){
    	if(StringUtils.isNoneEmpty(userId)&&StringUtils.isNoneEmpty(password)){
			String pwd = md5PasswordEncoder(password).substring(8, 24);
			return ecusAccountMapper.updatePasswdByUserid(userId, pwd);
		}
		return 0;
	}

	private String md5PasswordEncoder(String pwd) {
		return DigestUtils.md5DigestAsHex(pwd.getBytes());
	}

	public EcusAccountDTO queryUserInfo(String userId){
		return ecusAccountMapper.queryAccount(userId);
	}

	public void updateUserInfo(String userId,String userName,String fixedPhone,String shortPhone){
		ecusAccountMapper.updateUserInfo(userId, userName, fixedPhone, shortPhone);
	}

	public int getUserIdNum(String userId) {
		return ecusAccountMapper.getUserIdNum(userId);
	}

}


import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.stereotype.Component;



import java.util.Collection;
import java.util.LinkedHashMap;


@Log4j2
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
	@Autowired
	private ApplicationEventPublisher publisher;
	@Autowired
	private AccountManager accountManager;

	private final String expMsg="参数异常!";

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		LinkedHashMap<String,String> linkedHashMap= (LinkedHashMap<String, String>) authentication.getDetails();
		String loginType=linkedHashMap.get("login_type");
		String userId=linkedHashMap.get("username");
		String password = (String) authentication.getCredentials();
		if(StringUtils.isEmpty(loginType)||StringUtils.isEmpty(userId)||StringUtils.isEmpty(password)){
			throw OAuth2Exception.create(OAuth2Exception.ACCESS_DENIED,expMsg);
		}
		UserDetails user=accountManager.checkAccount(userId,password,accountManager.getLogonTypeEnum(loginType));
		Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
		return new UsernamePasswordAuthenticationToken(user, password, authorities);
	}

	@Override
	public boolean supports(Class<?> authentication) {
		return authentication.equals(UsernamePasswordAuthenticationToken.class);
	}
}
  1. 资源服务配置,资源服务器在请求中接受到token以后,拿着token去认证授权服务那校验token的有效性,和获取token对应的角色信息。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

@Configuration
@EnableResourceServer//开启资源服务
@ComponentScan("包路径")
public class ServerResouceConfig extends ResourceServerConfigurerAdapter {

//	@Autowired
//	private RedisConnectionFactory redisConnectionFactory;
//	@Autowired
//	private DenialAuthenticationEntryPoint denialAuthenticationEntryPoint;
//	@Autowired
//	private ServerAuthenticationSuccessHandler serverAuthenticationSuccessHandler;

	@Override
	public void configure(HttpSecurity httpSecurity) throws Exception {
		httpSecurity.formLogin().and().httpBasic().and()
			.authorizeRequests().antMatchers("/oauth/*","/business-track/*","/doc.html","/webjars/**",
				"/swagger-resources/**","/v2/api-docs-ext","/**.js","/file/**","/v2/api-docs","/scale-pic/download")
			.permitAll()
			.anyRequest().authenticated()//任何请求都需要身份认证
			.and()
			.logout()
			.deleteCookies("remove")
			.invalidateHttpSession(false)
			.logoutUrl("/logout")
			.permitAll();
			//.and()
			//.csrf().disable();//禁用CSRF
	}

	@Override
	public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
		resources.stateless(true)
			.tokenStore(tokenStore())
			.tokenServices(tokenServices()).resourceId("client");
		super.configure(resources);
	}


	@Bean
	public TokenStore tokenStore() {
		RedisStandaloneConfiguration redisStandaloneConfiguration=new RedisStandaloneConfiguration();
		redisStandaloneConfiguration.setDatabase(4);
		redisStandaloneConfiguration.setHostName("ip");
		RedisTokenStore redisTokenStore = new RedisTokenStore(new LettuceConnectionFactory(redisStandaloneConfiguration));
		redisTokenStore.setPrefix(".auth-token:");
		return redisTokenStore;
	}

	@Primary
	@Bean
	public RemoteTokenServices tokenServices() {
//		DefaultTokenServices tokenService=new DefaultTokenServices();
		final RemoteTokenServices tokenService = new RemoteTokenServices();
		tokenService.setCheckTokenEndpointUrl("ip:port/oauth/check_token");
		tokenService.setClientId("client");
		tokenService.setClientSecret("secret");
		return tokenService;
	}
}

网站文章

  • JS之去除数组中的数据(0, 空,undefined, null, false)

    1.Array.filter() arr.filter(Boolean) 2.通过遍历判断数组 空字符,undefined, null, false , 0转化为布尔类型都是 false; let a...

    2024-04-01 00:55:59
  • IDEA的 Debug窗口消失不见了的问题解决

    IDEA的 Debug窗口消失不见了的问题解决

    对于老用户来说,用起来不是很习惯,如何调出窗口呢?

    2024-04-01 00:55:52
  • bzoj4184 shallot(线段树时间分治+线性基)

    我们按时间建线段树,把所有数的存在时间求出来,一定是一个连续的区间,插到线段树对应区间上。线段树每个节点挂一个vector来存数。这样就把删除操作避免掉了。然后从上到下dfs一遍线段树,把每个叶子的线性基合并出来即可,复杂度O(nlognlogw)O(nlognlogw)O(nlognlogw) 空间O(nlogn+nlogw)O(nlogn+nlogw)O(nlogn+nlogw)#inc...

    2024-04-01 00:55:46
  • html5 学习之路 一(视频、音频、拖放)

    html5 基本概念:

    2024-04-01 00:55:21
  • python面向对象

    面向对象属性查找1.先从对象的名称空间找2.再从对象类的类变量找3.在找父类的类变量先对象本身-->类-->父类-->父类的父类-->object-->自己定制的元类-->typeclass people(): v_id=0 def __init__(self,name): self.name=namec...

    2024-04-01 00:55:15
  • easyheap

    easyheap

    首先,检查一下程序的保护机制,发现没开PIE,且got表可写然后,我们用IDA分析一下,发现没有show功能出现了很多sub_4008F1()函数我们一个个点开看一下,发现最后一个是创建,为了便于分析我们可以改成add(n)可以看出创建节点功能里,存在ptr[i]堆内容未初始化的漏洞经过分析,节点的结构体如下struct Obj{ char *Data; int Size...

    2024-04-01 00:55:06
  • 障碍物决策横纵向标签

    决策

    2024-04-01 00:55:00
  • SFP光纤笼子 别称 作用 性能要点 工程要素

    SFP光纤笼子 别称 作用 性能要点 工程要素

    2023年,Hqst盈盛电子于下属五金部开发生产SFP光纤连接器笼子等系列产品,所有产品生产及性标准都将参照连接器产品常用测试标准EIA-364-C等标准,以下为我司常规SFP光纤连接器基本性能要求。...

    2024-04-01 00:54:35
  • MySQL之深入InnoDB存储引擎——Buffer Pool

    MySQL之深入InnoDB存储引擎——Buffer Pool

    以这个算法为核心,InnoDB维护了一个LRU链表,如果页不在Buffer Pool中,在把该页从磁盘加载到Buffer Pool的时候,就把该缓冲页对应的控制块作为节点添加到LRU链表的头部,如果使...

    2024-04-01 00:54:27
  • 攻防世界Web新手区题解(超详细)

    攻防世界Web新手区题解(超详细)

    Web新手区题解

    2024-04-01 00:54:20