SpringSecurity实战笔记之OAuth
===================Spring Social OAuth================
一、app、小程序、前后端分离为什么使用OAuth协议
1、原有方法开发繁琐、安全性和客户体验差、有些前端技术不支持cookei,如小程序
2、好处:token自动生成,自定义校验,方便安全
二、Spring Security OAuth简介
1、服务提供商(Provider)提供认证服务器(Anthorization Server)及资源服务器(Resource Server)
2、认证服务器:包含四种授权模式(用户可以自定义认证方式)->生成存储Token
3、资源服务器:通过Spring Security过滤器链(添加OAuth2Authentication ProcessingFilter)保护资源
OAuth2Authentication ProcessingFilter 从请求中拿到Token,再通过指定的策略从token中读取用户相应的信息,再判断是否有权限读取资源
三、实现标准的OAuth服务提供商(查看框架https://oauth.net/2/ 页面中OAuth 2.0 Framework)
1、认证服务器
/** * 认证服务器,只需要添加@EnableAuthorizationServer */ @Configuration @EnableAuthorizationServer public class ImoocAuthorizationServerConfig { }
1.1、授权码模式(两步)
请求codeUrl /oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=all
return: http://example.com/?code=NZMOnL
请求tokenUrl method:POST /oauth/token
参数:
headers:
Content-Type:application/x-www-form-urlencoded
Authorization:Basic aW1vb2M6aW1vb2NzZWNyZXQ=
body:
grant_type:authorization_code
code:NZMOnL
redirect_uri:http://example.com
client_id:imooc
scope:all
return:
{
"access_token": "6a06dc0c-3e2c-4179-b1ee-7878a84b4f82",
"token_type": "bearer",
"refresh_token": "317b6725-593c-45e5-9aa1-fbe1b82172f0",
"expires_in": 43199,
"scope": "all"
}
1.2、密码模式(一步,适用于内部使用)
请求tokenUrl method:POST /oauth/token
参数:
headers:
Content-Type:application/x-www-form-urlencoded
Authorization:Basic aW1vb2M6aW1vb2NzZWNyZXQ=
body:
grant_type:password
username:user
password:123456
scope:all
return:
{
"access_token": "6a06dc0c-3e2c-4179-b1ee-7878a84b4f82",
"token_type": "bearer",
"refresh_token": "317b6725-593c-45e5-9aa1-fbe1b82172f0",
"expires_in": 42656,
"scope": "all"
}
2、资源服务器
/** * 资源服务器 */ @Configuration @EnableResourceServer public class ImoocResourceServerConfig { }
2.1、请求用户信息
url:/user/me hearders: Authorization:bearer 712a314c-4aef-46cf-8eb9-3269a3c554f0 return: { "password": null, "username": "user", "authorities": [ { "authority": "ROLE_USER" }, { "authority": "admin" } ], "accountNonExpired": true, "accountNonLocked": true, "credentialsNonExpired": true, "enabled": true, "userId": "user" }
注意点:
-用户必须有ROLE_USER角色才能访问
-clientId,clientSecret默认启动时分配,但可以配置
security.oauth2.client.client-id=imooc
security.oauth2.client.client-secret=imoocsecret
四、SpringSecurityOAuth核心源码解析
1、流程:获取令牌的请求(/oauth/token)
->TokenEndpoint
->ClientDetailsService(InMemoryClientDetailsService)
->ClientDetails
->TokenRequest
->TokenGranter(CompositeTokenGranter)
->OAuth2Request&Authentication
->OAuth2Authentication
->AuthorizationServerTokenServices(DefaultTokenServices)[TokenStore][TokenEnhancer]
->OAuth2AccessToken
五、重构3种登录
1、流程:登录请求
->XXXFilter
->登录逻辑处理
->AuthenticationSuccessHandler(以下功能在这个类中实现)
->OAuth2Request(ClientDetails(ClientDetailsService(ClientId)) 、TokenRequest(request))&Authentication
->OAuth2Authentication
->AuthorizationServerTokenServices(DefaultTokenServices)[TokenStore][TokenEnhancer]
->OAuth2AccessToken
2、OAuth2Request构造
2.1、ClientId->ClientDetailsService->ClientDetails&TokenRequest(request)
2.2、ClientId获取:(参照BasicAuthenticationFilter)从请求头部信息Authorization中获取Basic aW1vb2M6aW1vb2NzZWNyZXQ=
2.3、代码
//1、获取clientId String clientId = tokens[0]; String clientSecret = tokens[1]; //2、获取ClientDetails ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); //3、获取TokenRequest if(clientDetails == null){ throw new UnapprovedClientAuthenticationException("clientId对应的配置信息不存在:"+clientId); }else if(!StringUtils.equals(clientDetails.getClientSecret(),clientSecret)){ throw new UnapprovedClientAuthenticationException("clientSecret不匹配:"+clientId); } TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(), "custom"); //4、创建OAuth2Request OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
3、OAuth2Authentication构造
//5、创建OAuth2Authentication
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
4、安全配置
/** * 资源服务器 */ @Configuration @EnableResourceServer public class ImoocResourceServerConfig extends ResourceServerConfigurerAdapter{ @Autowired protected AuthenticationSuccessHandler imoocAuthenticationSuccessHandler; @Autowired protected AuthenticationFailureHandler imoocAuthenticationFailureHandler; @Autowired private SecurityProperties securityProperties; @Autowired private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; @Autowired private SpringSocialConfigurer imoocSocialSecurityConfig; @Override public void configure(HttpSecurity http) throws Exception { http.formLogin() .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL) .loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM) .successHandler(imoocAuthenticationSuccessHandler) .failureHandler(imoocAuthenticationFailureHandler); http /* .apply(validateCodeSecurityConfig) .and()*/ .apply(smsCodeAuthenticationSecurityConfig) .and() .apply(imoocSocialSecurityConfig) .and() .authorizeRequests() .antMatchers( SecurityConstants.DEFAULT_UNAUTHENTICATION_URL, SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, securityProperties.getBrowser().getSignInPage(), SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*", securityProperties.getBrowser().getSignUpUrl(), securityProperties.getBrowser().getSession().getSessionInvalidUrl(), securityProperties.getBrowser().getSignOutUrl(), "/user/regist" ).permitAll() .anyRequest() .authenticated() .and() .csrf().disable() ; } }
5、登录成功后请求资源
资源url:http://{{ip:port}}/user/me
headers:Authorization:bearer 80aaa952-230a-40a8-816b-c820c0100f5c(token)
六、重构验证码存储逻辑(同时解决了短信验证码登录及表单验证码登录)
1、之前短信验证图与图片验证码是存放在session中的,现在就不适用了
2、改为存放在redis中,生成时带上deviceId(存),校验时也带上deviceId(取)
接口:ValidateCodeRepository
browser中的实现类:SessionValidateCodeRepository
app中的实现类:RedisValidateCodeRepository
七、重构第三方登录
1、app通过openId(app通过简化模式获得openId)请求token
1.1、OpenIdAuthenticationToken
1.2、OpenIdAuthenticationFilter
1.3、OpenIdAuthenticationProvider
1.4、OpenIdAuthenticationSecurityConfig
1.5、将OpenIdAuthenticationSecurityConfig apply到ImoocResourceServerConfig中
1.6、登录
请求tokenUrl method:POST /authentication/openid
参数:
headers: Content-Type:application/x-www-form-urlencoded Authorization:Basic aW1vb2M6aW1vb2NzZWNyZXQ= body: openId:password providerId:8349FC568B3A166DC4FEB6EC934145F0 password:qq return: { "access_token": "6a06dc0c-3e2c-4179-b1ee-7878a84b4f82", "token_type": "bearer", "refresh_token": "317b6725-593c-45e5-9aa1-fbe1b82172f0", "expires_in": 42656, "scope": "all" }
2、app通过第三方code(app通过授权模式获得code)请求token
2.1、直接将第三方认证返回的带有code的url重定向到后台
http://www.pinzhi365.com/auth/weixin?code=0712E5f01GSZHZ11nsg01PgSe012E5fz&state=606d9f50-2dae-45fb-93e1-cc3a232543b7
2.2、后台就会接着到换取token.............
2.3、成功后,要进入到APP中的成功处理器中
ImoocSpringSocialConfigurer中filter要指定成功处理器为APP的成功处理器
八、重构注册逻辑
1、浏览器环境下,在用户第一次登录的情况下,会将用户的第三方信息保存在session中,再跳转到注册页面,
用户注册时再通过providerSignUtils工具的doPostSignUp(userId,new ServletRequest(request))进行绑定
2、app中的解决思路就是,在用户第一次登录的情况下,会将用户的第三方信息保存在redis(key中加入设备id),然后通知前端引导用户去注册,
用户注册时再通过自定义的AppSignUtils工具的doPostSignUp(userId,new ServletRequest(request))进行绑定
3、更改注册路由
/** * @author zhailiang * 实现BeanPostProcessor,可以在Bean初始化之前与初始化之后进行操作 * */ @Component public class SpringSocialConfigurerPostProcessor implements BeanPostProcessor { /* (non-Javadoc) * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String) */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /* (non-Javadoc) * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String) */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(StringUtils.equals(beanName, "imoocSocialSecurityConfig")){ ImoocSpringSocialConfigurer config = (ImoocSpringSocialConfigurer)bean; config.signupUrl(SecurityConstants.DEFAULT_SOCIAL_USER_INFO_URL); return config; } return bean; } }
4、AppSignUtils代码
@Component public class AppSignUpUtils { @Autowired private RedisTemplate<Object,Object> redisTemplate; @Autowired private ConnectionFactoryLocator connectionFactoryLocator; @Autowired private UsersConnectionRepository usersConnectionRepository; public void sveConnectionData(WebRequest request, ConnectionData connectionData){ redisTemplate.opsForValue().set(getKey(request),connectionData,10, TimeUnit.MINUTES); } /** * 缓存社交网站用户信息到redis * @param request * @param connectionData */ public void saveConnectionData(WebRequest request, ConnectionData connectionData) { redisTemplate.opsForValue().set(getKey(request), connectionData, 10, TimeUnit.MINUTES); } /** * 将缓存的社交网站用户信息与系统注册用户信息绑定 * @param request * @param userId */ public void doPostSignUp( String userId,WebRequest request) { String key = getKey(request); if(!redisTemplate.hasKey(key)){ throw new AppSecretException("无法找到缓存的用户社交账号信息"); } ConnectionData connectionData = (ConnectionData) redisTemplate.opsForValue().get(key); Connection<?> connection = connectionFactoryLocator.getConnectionFactory(connectionData.getProviderId()) .createConnection(connectionData); usersConnectionRepository.createConnectionRepository(userId).addConnection(connection); redisTemplate.delete(key); } private String getKey(WebRequest request) { String deviceId = request.getHeader("deviceId"); if(StringUtils.isBlank(deviceId)){ throw new AppSecretException("设备Id参数不能为空"); } return "imooc:security:social.connect." + deviceId; } }
5、AppSecurityController代码
@RestController public class AppSecurityController extends SocialController{ @Autowired private ProviderSignInUtils providerSignInUtils; @Autowired private AppSignUpUtils AppSignUpUtils; /** * 需要注册时跳到这里,返回401和用户信息给前端 * @param request * @return */ @GetMapping(SecurityConstants.DEFAULT_SOCIAL_USER_INFO_URL)//要进行注册时跳转的url @ResponseStatus(HttpStatus.UNAUTHORIZED) public SocialUserInfo getSocialUserInfo(HttpServletRequest request) { Connection<?> connection = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request)); AppSignUpUtils.saveConnectionData(new ServletWebRequest(request), connection.createData()); return buildSocialUserInfo(connection); } }
6、注册controller
@RequestMapping("/user") public class UserController { @Autowired private AppSignUpUtils appSignUpUtils; //更新imooc_userconnection @PostMapping("/regist") public Object regist(User user,HttpServletRequest request){ //不管是注册用户还是绑定用户,都会拿到用户的唯一标识 String userName = user.getUsername(); //浏览器 // providerSignInUtils.doPostSignUp(userName,new ServletWebRequest(request)); //App appSignUpUtils.doPostSignUp(userName,new ServletWebRequest(request)); return user; } }
九、令牌配置
1、ImoocAuthorizationServerConfig 继承 AuthorizationServerConfigurerAdapter进行相应的配置
-重写相应的方法
/** * tokenKey的访问权限表达式配置 */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { super.configure(security); } /** * 客户端配置 */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { InMemoryClientDetailsServiceBuilder builder = clients.inMemory(); if (ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) { for (OAuth2ClientProperties client : securityProperties.getOauth2().getClients()) { builder.withClient(client.getClientId()) .secret(client.getClientSecret()) .authorizedGrantTypes("refresh_token", "authorization_code", "password") .accessTokenValiditySeconds(client.getAccessTokenValidateSeconds()) .refreshTokenValiditySeconds(2592000) .scopes("all"); } } } /** * 认证及token配置 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); }
2、定义tokenStore
@Configuration public static class RedisConfig { @Autowired private RedisConnectionFactory redisConnectionFactory; /** * @return */ @Bean public TokenStore redisTokenStore() { return new RedisTokenStore(redisConnectionFactory); } }
3、属性类:OAuth2Properties、OAuth2ClientProperties
4、application.properties
#认证服务器注册的第三方应用配置项,参见OAuth2ClientProperties
imooc.security.oauth2.clients[0].clientId = imooc
imooc.security.oauth2.clients[0].clientSecret = imoocsecret
imooc.security.oauth2.clients[0].accessTokenValidateSeconds = 3600
imooc.security.oauth2.clients[1].clientId = test
imooc.security.oauth2.clients[1].clientSecret = test
十、使用JWT替换默认令牌
1、TokenStoreConfig中定义TokenStore,为了与redisTokenStore不冲突,要使用@ConditionalOnProperty注解进行处理
/** * 使用jwt时的配置,默认生效 * * @author zhailiang * */ @Configuration @ConditionalOnProperty(prefix = "imooc.security.oauth2", name = "tokenStore", havingValue = "jwt", matchIfMissing = true) public static class JwtConfig { @Autowired private SecurityProperties securityProperties; /** * jwt存储 * @return */ @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } /** * jwttoken生成 * @return */ @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey(securityProperties.getOauth2().getJwtSigningKey());//使用jwt时为token签名的秘钥 return converter; } /** * jwt token额外信息添加,可以通过实现TokenEnhancer接口,添加额外信息 * @return */ @Bean @ConditionalOnBean(TokenEnhancer.class) public TokenEnhancer jwtTokenEnhancer(){ return new TokenJwtEnhancer(); } }
2、ImoocAuthorizationServerConfig配置
@Autowired(required = false) private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired(required = false) private TokenEnhancer jwtTokenEnhancer; /** * 认证及token配置 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); //以下是与jwttoken生成及额外信息添加设置 if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) { TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> enhancers = new ArrayList<>(); enhancers.add(jwtTokenEnhancer); enhancers.add(jwtAccessTokenConverter); enhancerChain.setTokenEnhancers(enhancers); endpoints.tokenEnhancer(enhancerChain).accessTokenConverter(jwtAccessTokenConverter); } }
3、jwtToken中包含用户信息,并且可以http://www.jsonwebtoken.io/上进行解析
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImNvbXBhbnkiOiJpbW9vYyIsImV4cCI6MTUxMjU3ODEwNiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiZjFlNzI1YjgtOTdmNC00YmUzLThmNjAtNmE2MzA4N2NmMmJmIiwiY2xpZW50X2lkIjoiaW1vb2MifQ.TH1upg0M1x2U9WlhKNaLqE4jmKf3ey1X2aji9vN_mig", "token_type": "bearer", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImYxZTcyNWI4LTk3ZjQtNGJlMy04ZjYwLTZhNjMwODdjZjJiZiIsImNvbXBhbnkiOiJpbW9vYyIsImV4cCI6MTUxNTE2NTgzNywiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiOWJmNDVmNGItZDhjNi00ZWNmLTg0M2MtZTJiNzdmYTk4NzI3IiwiY2xpZW50X2lkIjoiaW1vb2MifQ.mNDpVpY3DfV_6sJrxPfcsdEuCo65PYkH66zXdA0xrJ4", "expires_in": 3599, "scope": "all", "company": "imooc", "jti": "f1e725b8-97f4-4be3-8f60-6a63087cf2bf" }
4、额外添加的信息,Authentication中并不会包含,所以为自己解析token获取
4.1、引用jar
<!--解析jwtToken--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency>
4.2、使用
@GetMapping("/me") public Object getCurrentUser(Authentication user, HttpServletRequest request) throws UnsupportedEncodingException { String token = StringUtils.substringAfter(request.getHeader("Authorization"), "bearer "); //io.jsonwebtoken.Jwts默认是使用ios。。,所以要指定编码为utf-8 Claims claims = Jwts.parser().setSigningKey(securityProperties.getOauth2().getJwtSigningKey().getBytes("UTF-8")) .parseClaimsJws(token).getBody(); String company = (String) claims.get("company"); System.out.println(company); return user; }
十一、刷新令牌post
url:http://{{ip:port}}/oauth/token
headers: Content-Type:application/x-www-form-urlencoded Authorization:Basic aW1vb2M6aW1vb2NzZWNyZXQ= body: grant_type:refresh_token refresh_token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6IjRjYmVhNGYzLTkxZWUtNDk0Ny1hYzk4LTVkNTc1ZDAxNWRhYyIsImNvbXBhbnkiOiJpbW9vYyIsImV4cCI6MTUxNTE2NTgzNywiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiOWJmNDVmNGItZDhjNi00ZWNmLTg0M2MtZTJiNzdmYTk4NzI3IiwiY2xpZW50X2lkIjoiaW1vb2MifQ.s5DEvHpu4BOKJnkr2xSgCIGarZmynO6AiQTdEj8Q9wQ scope:all return: { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImNvbXBhbnkiOiJpbW9vYyIsImV4cCI6MTUxMjU3ODEwNiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiZjFlNzI1YjgtOTdmNC00YmUzLThmNjAtNmE2MzA4N2NmMmJmIiwiY2xpZW50X2lkIjoiaW1vb2MifQ.TH1upg0M1x2U9WlhKNaLqE4jmKf3ey1X2aji9vN_mig", "token_type": "bearer", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImYxZTcyNWI4LTk3ZjQtNGJlMy04ZjYwLTZhNjMwODdjZjJiZiIsImNvbXBhbnkiOiJpbW9vYyIsImV4cCI6MTUxNTE2NTgzNywiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiOWJmNDVmNGItZDhjNi00ZWNmLTg0M2MtZTJiNzdmYTk4NzI3IiwiY2xpZW50X2lkIjoiaW1vb2MifQ.mNDpVpY3DfV_6sJrxPfcsdEuCo65PYkH66zXdA0xrJ4", "expires_in": 3599, "scope": "all", "company": "imooc", "jti": "f1e725b8-97f4-4be3-8f60-6a63087cf2bf" }
十二、基于JWT实现SSO单点登录
1、认证服务器
1.1、继承AuthorizationServerConfigurerAdapter进行客户端,token生成,安全配置
@Configuration @EnableAuthorizationServer public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("imooc1") .secret("imoocsecrect1") .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("all") .and() .withClient("imooc2") .secret("imoocsecrect2") .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("all"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("isAuthenticated()"); } @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAc cessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("imooc"); return converter; } }
1.2、使用表单认证替base认证
-SsoUserDetailsService
@Component public class SsoUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; /* (non-Javadoc) * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String) */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return new User(username, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); } }
-SsoSecurityConfig
@Configuration public class SsoSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin().and().authorizeRequests().anyRequest().authenticated(); } }
1.3、spring security OAuth2默认在认证成功后跳到授权页面,要用户点击授权才可以访问资源
这个页面由@FrameworkEndpoint WhitelabelApprovalEndpoint中定义,可以通过自定义一个WhitelabelApprovalEndpoint并使用@RestController可以覆盖原来的controller
需要新增相关的类有:SsoApprovalEndpoint(复制于WhitelabelApprovalEndpoint,修改template添加<script>document.getElementById('confirmationForm').submit()</script>
SsoSpelView
1.4、Application.properties中
server.port = 9999
server.context-path = /server
2、应用1(应用2与应用1相似,将1更改为2)
2.1、启动main方法添加@EnableOAuth2Sso注解
@SpringBootApplication @EnableOAuth2Sso @RestController public class SsoClient1Application { @GetMapping("/user") public Authentication user(Authentication user) { return user; } public static void main(String[] args) { SpringApplication.run(SsoClient1Application.class, args); } }
2.2、Application.properties中
security.oauth2.client.clientId = imooc1
security.oauth2.client.clientSecret = imoocsecrect1
security.oauth2.client.user-authorization-uri = http://127.0.0.1:9999/server/oauth/authorize
security.oauth2.client.access-token-uri = http://127.0.0.1:9999/server/oauth/token
security.oauth2.resource.jwt.key-uri = http://127.0.0.1:9999/server/oauth/token_key
server.port = 8080
server.context-path = /client1
2.3、建一个index.html,用于跳转到client2应用中
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>SSO Client2</title> </head> <body> <h1>SSO Demo Client2</h1> <a href="http://127.0.0.1:8080/client1/index.html">访问Client1</a> </body> </html>
本文来自博客园,作者:咔咔皮卡丘,转载请注明原文链接:https://www.cnblogs.com/anquing/p/17641138.html