赞
踩
项目基于springcloud,授权决定使用提供的spring-oauth2,了解了oauth2原理之后,基于业务有很多需要重写的点;
1.获取token同时需要获取客户端信息(手机型号,app版本号等)
2.除了提供的password方式登录,还需要提供验证码登录功能
对spring-cloud-starter-oauth2包的源码进行了简单的了解:
自定义TokenGranter可以实现获取除了默认参数的其他参数,同时可以增加验证
DaoAuthenticationProvider是默认的实现类,通过重写provider来自定义校验逻辑
自定义granter
- package com.mlines.cloud.granter;
-
- import com.mlines.cloud.token.AppAuthenticationToken;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.security.authentication.AbstractAuthenticationToken;
- import org.springframework.security.authentication.AccountStatusException;
- import org.springframework.security.authentication.AuthenticationManager;
- import org.springframework.security.authentication.BadCredentialsException;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
- import org.springframework.security.oauth2.provider.*;
- import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
- import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
-
- import java.util.LinkedHashMap;
- import java.util.Map;
-
- /**
- * 短信验证码方式
- * zhangmx
- */
- public class SMSCodeTokenGranter extends AbstractTokenGranter {
- private static final String GRANT_TYPE = "smscode";
-
- private final AuthenticationManager authenticationManager;
-
- public SMSCodeTokenGranter(AuthenticationManager authenticationManager,
- AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
- this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
- }
-
- protected SMSCodeTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
- ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
- super(tokenServices, clientDetailsService, requestFactory, grantType);
- this.authenticationManager = authenticationManager;
- }
-
-
- /**
- * 在这个方法可以进行验证码等其他操作
- * @param client
- * @param tokenRequest
- * @return
- */
- @Override
- protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
-
- Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
- String username = parameters.get("username");
- String smscode = parameters.get("smscode");
- String deviceId=parameters.get("deviceId");
- String type=parameters.get("type");
- String sysVersion=parameters.get("sysVersion");
- String deviceVersion=parameters.get("deviceVersion");
- String appVersion=parameters.get("appVersion");
- parameters.put("loginType","sms");
- // Protect from downstream leaks of password
- parameters.remove("password");
-
- //校验验证码
- if(!StringUtils.equals(smscode,"1001")){
- throw new InvalidGrantException("验证码不正确");
- }
-
- Authentication userAuth = new AppAuthenticationToken(username, smscode,type,deviceId,sysVersion,deviceVersion,appVersion);
- ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
- try {
- userAuth = authenticationManager.authenticate(userAuth);
- }
- catch (AccountStatusException ase) {
- //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
- throw new InvalidGrantException(ase.getMessage());
- }
- catch (BadCredentialsException e) {
- // If the username/password are wrong the spec says we should send 400/invalid grant
- throw new InvalidGrantException(e.getMessage());
- }
- if (userAuth == null || !userAuth.isAuthenticated()) {
- throw new InvalidGrantException("Could not authenticate user: " + username);
- }
-
- OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
- return new OAuth2Authentication(storedOAuth2Request, userAuth);
- }
- }
自定义provider:
- package com.mlines.cloud.provider;
-
-
- import com.mlines.cloud.feign.client.SysUserClient;
- import com.mlines.cloud.token.AppAuthenticationToken;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.security.authentication.BadCredentialsException;
- import org.springframework.security.authentication.InternalAuthenticationServiceException;
- import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
- import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
- import org.springframework.security.core.AuthenticationException;
- 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.crypto.password.PasswordEncoder;
- import org.springframework.util.Assert;
-
- import java.util.Map;
-
- /**
- * Created by fp295 on 2018/11/25.4
- * 用户名密码登录
- */
- public class AppAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
-
- @Autowired
- private UserDetailsService userDetailsService;
- @Autowired
- private SysUserClient sysUserClient;
-
- private PasswordEncoder passwordEncoder;
- // /**
- // * 根据用户名取回用户信息后,进入此方法判断密码是否匹配,如果是验证码登录 判断验证码是否正确
- // * @param var1
- // * @param authentication
- // * @throws AuthenticationException
- // */
- // @Override
- // protected void additionalAuthenticationChecks(UserDetails var1, Authentication authentication) throws AuthenticationException {
- //
- // if(authentication.getCredentials() == null) {
- // this.logger.debug("Authentication failed: no credentials provided");
- // throw new BadCredentialsException(this.messages.getMessage("AppAuthenticationProvider.badCredentials", "Bad credentials"));
- // } else {
- // String presentedPassword = authentication.getCredentials().toString();
- // Map<String,Object> otherParams=(Map<String,Object>)authentication.getDetails();//其他参数
- // String loginType=(String)otherParams.get("loginType");
- // if(StringUtils.equals(loginType,"password")){//密码方式登录
- // if (!passwordEncoder.matches(presentedPassword, var1.getPassword())) {
- // logger.debug("密码错误");
- //
- // throw new BadCredentialsException(messages.getMessage(
- // "400",
- // "密码错误"));
- // }
- // }
- //
-
- // 验证码验证,调用公共服务查询 key 为authentication.getPrincipal()的value, 并判断其与验证码是否匹配
- if(!"1000".equals(presentedPassword)){
- this.logger.debug("Authentication failed: verifyCode does not match stored value");
- throw new BadCredentialsException(this.messages.getMessage("AppAuthenticationProvider.badCredentials", "Bad verifyCode"));
- }
- // }
- // }
- //
- //
- //
- // @Override
- // protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user,String deviceId) {
- // AppAuthenticationToken result = new AppAuthenticationToken(principal, authentication.getCredentials(), user.getAuthorities(),deviceId);
- // result.setDetails(authentication.getDetails());
- // return result;
- // }
- //
- // @Override
- // protected UserDetails retrieveUser(String phone, Authentication authentication) throws AuthenticationException {
- // UserDetails loadedUser;
- // try {
- // loadedUser = userDetailsService.loadUserByUsername(phone);
- // } catch (UsernameNotFoundException var6) {
- // throw var6;
- // } catch (Exception var7) {
- // throw new InternalAuthenticationServiceException(var7.getMessage(), var7);
- // }
- //
- // if(loadedUser == null) {
- // throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
- // } else {
- // return loadedUser;
- // }
- // }
-
- @Override
- protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
- if(authentication.getCredentials() == null) {
- this.logger.debug("Authentication failed: no credentials provided");
- throw new BadCredentialsException(this.messages.getMessage("AppAuthenticationProvider.badCredentials", "Bad credentials"));
- } else {
- String presentedPassword = authentication.getCredentials().toString();
- Map<String,Object> otherParams=(Map<String,Object>)authentication.getDetails();//其他参数
- String loginType=(String)otherParams.get("loginType");
- if(StringUtils.equals(loginType,"password")){//密码方式登录
- if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
- logger.debug("密码错误");
-
- throw new BadCredentialsException(messages.getMessage(
- "400",
- "密码错误"));
- }
- }
-
- //
- // // 验证码验证,调用公共服务查询 key 为authentication.getPrincipal()的value, 并判断其与验证码是否匹配
- // if(!"1000".equals(presentedPassword)){
- // this.logger.debug("Authentication failed: verifyCode does not match stored value");
- // throw new BadCredentialsException(this.messages.getMessage("AppAuthenticationProvider.badCredentials", "Bad verifyCode"));
- // }
- }
- }
-
- @Override
- protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
- UserDetails loadedUser;
- try {
- loadedUser = userDetailsService.loadUserByUsername(username);
- } catch (UsernameNotFoundException var6) {
- throw var6;
- } catch (Exception var7) {
- throw new InternalAuthenticationServiceException(var7.getMessage(), var7);
- }
-
- if(loadedUser == null) {
- throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
- } else {
- return loadedUser;
- }
- }
-
- @Override
- public boolean supports(Class<?> authentication) {
- return AppAuthenticationToken.class.isAssignableFrom(authentication);
- }
-
- public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
- Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
- this.passwordEncoder = passwordEncoder;
- }
-
- protected PasswordEncoder getPasswordEncoder() {
- return passwordEncoder;
- }
-
- //
- //
- public UserDetailsService getUserDetailsService() {
- return userDetailsService;
- }
-
- public void setUserDetailsService(UserDetailsService userDetailsService) {
- this.userDetailsService = userDetailsService;
- }
- }
其他代码片段
- package com.mlines.cloud.config;
-
- import com.mlines.cloud.granter.AppResourceOwnerPasswordTokenGranter;
- import com.mlines.cloud.granter.SMSCodeTokenGranter;
- import com.mlines.cloud.handler.CustomWebResponseExceptionTranslator;
- import com.mlines.cloud.provider.AppAuthenticationProvider;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.security.authentication.AuthenticationManager;
- import org.springframework.security.authentication.AuthenticationProvider;
- import org.springframework.security.authentication.ProviderManager;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
- 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.ClientDetailsService;
- import org.springframework.security.oauth2.provider.CompositeTokenGranter;
- import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
- import org.springframework.security.oauth2.provider.TokenGranter;
- import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
- import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
- import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
- import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
- import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
- import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
- import org.springframework.security.oauth2.provider.token.TokenEnhancer;
- import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
- import org.springframework.security.oauth2.provider.token.TokenStore;
- import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
- import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
-
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.List;
-
- @Configuration
- @EnableAuthorizationServer
- public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter {
- @Autowired
- private AuthenticationManager authenticationManager;
- @Autowired
- private CustomAccessDeniedHandler customAccessDeniedHandler;
- @Autowired
- private CustomWebResponseExceptionTranslator customWebResponseExceptionTranslator;
- // @Autowired
- // private UsernameUserDetailService userDetailsService;
- @Autowired
- private UserDetailsService userDetailsService;//读取客户端的service app可以是一个客户端 web可以是一个客户端
- @Override
- public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
- clients.inMemory()
- .withClient("web-app")
- .secret( new BCryptPasswordEncoder().encode("rq9nuNkIwT"))
- .scopes("web-app")
- .authorizedGrantTypes("password","smscode", "refresh_token")
- .accessTokenValiditySeconds(86400)//超时
- .refreshTokenValiditySeconds(604800)
- .and()
- .withClient("android-app")
- .secret(new BCryptPasswordEncoder().encode("ijnuybdsvv"))
- .scopes("android-app")
- .authorizedGrantTypes("password","smscode", "refresh_token")
- .accessTokenValiditySeconds(86400*7)
- .refreshTokenValiditySeconds(604800*4);
- }
-
- @Bean
- public UserDetailsService userDetailsService(){
- return userDetailsService;
- }
-
- @Bean
- public JwtAccessTokenConverter jwtAccessTokenConverter() {
- JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
- jwtAccessTokenConverter.setSigningKey("cloudserver");
- return jwtAccessTokenConverter;
- }
-
- public static void main(String[] args){
- // Jwt jwt=JwtHelper.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtc2ciOiLnmbvlvZXmiJDlip8iLCJjb2RlIjoyMDAsImRhdGEiOnsibGFiZWwiOiLlvKDlrablj4siLCJpZCI6Mn0sInVzZXJfbmFtZSI6IjE2NjY2NjY2NjIxIiwic2NvcGUiOlsid2ViLWFwcCJdLCJleHAiOjE1NTA3NDc1NTQsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiJhZDk1OGZhMC1mYWNiLTQyOGQtYWZlMi0xNGQ0OGJhMTBjMjAiLCJjbGllbnRfaWQiOiJ3ZWItYXBwIn0.yQbgx8rXCkwqdYWhGOBIRJZyIe9VOKrV3yNIcqVE294");
- // System.out.println(new String(jwt.getClaims().getBytes()));
- System.out.println(new BCryptPasswordEncoder().encode("123456"));
- System.out.println(new BCryptPasswordEncoder().matches("123456","$2a$10$xT8XiML30Yer0d3cYBmXfewMgZtQyiGc25zZs9ipkvKJuyOQDwcF2"));
- }
-
- @Bean
- public TokenStore jwtTokenStore() {
- return new JwtTokenStore(jwtAccessTokenConverter());
- }
-
- @Override
- public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
- TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
- enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), jwtAccessTokenConverter()));
- endpoints.tokenStore(jwtTokenStore())
- .accessTokenConverter(jwtAccessTokenConverter())
- .reuseRefreshTokens(false)//该字段设置设置refresh token是否重复使用,true:reuse;false:no reuse.
- .authenticationManager(authenticationManager)
- .userDetailsService(userDetailsService)
- .tokenEnhancer(enhancerChain);
- endpoints.exceptionTranslator(customWebResponseExceptionTranslator);
- endpoints.tokenGranter(new CompositeTokenGranter(getTokenGranters(endpoints)));
- }
-
- private List<TokenGranter> getTokenGranters(AuthorizationServerEndpointsConfigurer endpoints){
- ClientDetailsService clientDetails = endpoints.getClientDetailsService();
- AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices();
- AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices();
- OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory();
- // ((DefaultTokenServices)tokenServices).setAuthenticationManager(new ProviderManager(getProvider(),null));
- List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
- tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails,
- requestFactory));
- tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
- ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);
- tokenGranters.add(implicit);
- tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
- if (authenticationManager != null) {
- // tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
- // clientDetails, requestFactory));//有了自定义的用户名密码验证方式不需要初始化默认的了
- tokenGranters.add(new AppResourceOwnerPasswordTokenGranter(new ProviderManager(getProvider(),null),tokenServices,endpoints.getClientDetailsService(),endpoints.getOAuth2RequestFactory()));
- tokenGranters.add(new SMSCodeTokenGranter(new ProviderManager(getProvider(),null),tokenServices,endpoints.getClientDetailsService(),endpoints.getOAuth2RequestFactory()));
-
- }
- //添加自定义granter
- return tokenGranters;
- }
-
- private List<AuthenticationProvider> getProvider(){
- List<AuthenticationProvider> list=new ArrayList<>();
- AppAuthenticationProvider provider = new AppAuthenticationProvider();
- // 设置userDetailsService
- provider.setUserDetailsService(userDetailsService);
- provider.setPasswordEncoder(new BCryptPasswordEncoder());
- // 禁止隐藏用户未找到异常
- provider.setHideUserNotFoundExceptions(false);
- list.add(provider);
- return list;
- }
-
- @Override
- public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
- security.allowFormAuthenticationForClients()
- .authenticationEntryPoint(new AuthExceptionEntryPoint())//自定义异常信息
- .accessDeniedHandler(customAccessDeniedHandler)
- .tokenKeyAccess("permitAll()")
- .checkTokenAccess("isAuthenticated()");
- }
-
-
-
- @Bean
- public TokenEnhancer customTokenEnhancer() {
- return new CustomTokenEnhancer();// 写入自定义信息
- }
- //
- // @Override
- // public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
- // oauthServer.authenticationEntryPoint(new AuthExceptionEntryPoint());
- // }
-
- }
- package com.mlines.cloud.config;
-
- import com.mlines.cloud.filter.AppLoginAuthenticationFilter;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- 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.configuration.EnableWebSecurity;
- import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-
- @Configuration
- @EnableWebSecurity
- public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
- //
- @Autowired
- private UserDetailsService userDetailsService;
-
- @Bean
- public BCryptPasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
-
- // @Override
- // protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- // auth
- // .userDetailsService(userDetailsService())
- // .passwordEncoder(passwordEncoder());
- // }
-
-
-
-
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- //TODO:use md5
- // auth.authenticationProvider(appAuthenticationProvider());
- auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
-
- }
- //不定义没有password grant_type
- @Override
- @Bean
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
- //
- @Bean
- public AppLoginAuthenticationFilter getPhoneLoginAuthenticationFilter() {
- AppLoginAuthenticationFilter filter = new AppLoginAuthenticationFilter();
- try {
- filter.setAuthenticationManager(this.authenticationManagerBean());
- } catch (Exception e) {
- e.printStackTrace();
- }
- // filter.setAuthenticationSuccessHandler(new LoginAuthSuccessHandler());
- return filter;
- }
- // /**
- // * 手机验证码登陆过滤器
- // * @return
- // */
- // @Bean
- // public UserNamePwdAuthenticationFilter getUserNamePwdFilter() {
- // UserNamePwdAuthenticationFilter filter = new UserNamePwdAuthenticationFilter();
- // try {
- // filter.setAuthenticationManager(this.authenticationManagerBean());
- // } catch (Exception e) {
- // e.printStackTrace();
- // }
- // return filter;
- // }
-
- }
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。