实现WebSecurityConfigurerAdapter
重写其中的configure(HttpSecurity http)方法
配置登陆页面以及登陆请求url等参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired(required = false) private AuthenticationEntryPoint authenticationEntryPoint;
@Resource private UserDetailsService userDetailsService;
@Autowired private PasswordEncoder passwordEncoder;
@Resource private LogoutHandler oauthLogoutHandler;
@Autowired private OpenIdAuthenticationSecurityConfig openIdAuthenticationSecurityConfig;
@Autowired private MobileAuthenticationSecurityConfig mobileAuthenticationSecurityConfig;
@Autowired private MobileAuthrnticationCodeSecurityConfig mobileAuthrnticationCodeSecurityConfig;
@Autowired private PasswordAuthrnticationCodeSecurityConfig passwordAuthrnticationCodeSecurityConfig;
@Autowired private AuthenticationManager authenticationManager;
@Autowired private TenantAuthenticationSecurityConfig tenantAuthenticationSecurityConfig;
@Autowired private TenantProperties tenantProperties;
/** * 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户 * @return 认证管理对象 */ @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() //授权服务器关闭basic认证 .permitAll() .and() .logout() .logoutUrl(SecurityConstants.LOGOUT_URL) .logoutSuccessHandler(new OauthLogoutSuccessHandler()) .addLogoutHandler(oauthLogoutHandler) .clearAuthentication(true) .and() .apply(openIdAuthenticationSecurityConfig) .and() .apply(mobileAuthenticationSecurityConfig) .and() .apply(passwordAuthrnticationCodeSecurityConfig) .and() .apply(mobileAuthrnticationCodeSecurityConfig) .and() .addFilterBefore(new LoginProcessSetTenantFilter(), UsernamePasswordAuthenticationFilter.class) .csrf().disable() // 解决不允许显示在iframe的问题 .headers().frameOptions().disable().cacheControl(); http.formLogin() .loginPage(SecurityConstants.LOGIN_PAGE) .loginProcessingUrl(SecurityConstants.OAUTH_LOGIN_PRO_URL) .successHandler(authenticationSuccessHandler);
// 基于密码 等模式可以无session,不支持授权码模式 if (authenticationEntryPoint != null) { http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } else { // 授权码模式单独处理,需要session的支持,此模式可以支持所有oauth2的认证 http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED); } }
} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
@Configuration @EnableAuthorizationServer @AutoConfigureAfter(AuthorizationServerEndpointsConfigurer.class) public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 注入authenticationManager 来支持 password grant type */ @Autowired private AuthenticationManager authenticationManager;
@Resource private UserDetailsService userDetailsService;
@Autowired private TokenStore tokenStore;
@Autowired private WebResponseExceptionTranslator webResponseExceptionTranslator;
@Autowired private RedisClientDetailsService clientDetailsService;
@Autowired private RandomValueAuthorizationCodeServices authorizationCodeServices;
@Autowired private TokenGranter tokenGranter;
/** * 配置身份认证器,配置认证方式,TokenStore,TokenGranter,OAuth2RequestFactory * @param endpoints */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.tokenStore(tokenStore) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .authorizationCodeServices(authorizationCodeServices) .exceptionTranslator(webResponseExceptionTranslator) .tokenGranter(tokenGranter); }
/** * 配置应用名称 应用id * 配置OAuth2的客户端相关信息 * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(clientDetailsService); clientDetailsService.loadAllClientToCache(); }
/** * 对应于配置AuthorizationServer安全认证的相关信息,创建ClientCredentialsTokenEndpointFilter核心过滤器 * @param security */ @Override public void configure(AuthorizationServerSecurityConfigurer security) { security .tokenKeyAccess("isAuthenticated()") .checkTokenAccess("permitAll()") //让/oauth/token支持client_id以及client_secret作登录认证 .allowFormAuthenticationForClients(); } } |
需要传入grant_type,deviceId,username,password
例如请求是这样的:
1 2 3 4 5 6 7 |
https://oauth.b.com/oauth/token? grant_type=password& # 授权方式是"密码式" username=USERNAME& password=PASSWORD& client_id=CLIENT_ID& client_secret=123123& scope=all |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
//账号密码登陆 var grant_type = 'password_code'; var deviceId = $("input[name=deviceId]").val(); var validCode = $("input[name=validCode]").val(); layer.load(2); var clients = $("#clients").attr("value");
var loginData ={"clients":clients,"grant_type":grant_type,"username":username,"password":hex_md5(password) ,"deviceId":deviceId,"validCode":validCode} config.putApp(clients); $.ajax({ url: config.base_server + '/oauth/token', xhrFields: { withCredentials: true }, data: loginData, type: 'POST', beforeSend: function (xhr) { xhr.setRequestHeader('Authorization', 'Basic ' + window.btoa(config.clientId + ":" + config.clientSecret)); }, success: function (data) { if (data.resp_code === 0) { config.putToken(data.datas); layer.msg('登录成功', {icon: 1, time: 500}, function () { location.replace('./'); }); } else { layer.closeAll('loading'); layer.msg(data.resp_msg, {icon: 5, time: 500}); } }, error: function (xhr) { layer.closeAll('loading'); //区分错误信息 //验证码错误 if(xhr.responseJSON.error === 'invalid_grant'){ layer.msg(xhr.responseJSON.resp_msg, {icon: 5, time: 500}); }else if(xhr.responseJSON.error === 'unsupported_response_type'){ //账号错误 var win = layer.open({ content:'<div>该账号已经被系统锁定或者禁用,<br>如需帮助请及时联系系统管理员进行处理!</div>' ,btn: ['确定'] ,btnAlign: 'c' ,closeBtn: 0 ,yes: function(index, layero){ layer.close(win); } }); }else { layer.msg(xhr.responseJSON.resp_msg, {icon: 5, time: 500}); } var src = $(".login-code").attr("src"); $(".login-code").attr("src", src + '?t=' + (new Date).getTime()); } }); //阻止表单跳转 return false; |
可以新增一个配置类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
@Configuration public class TokenGranterConfig { @Autowired private ClientDetailsService clientDetailsService;
@Autowired private UserDetailsService userDetailsService;
@Autowired private AuthenticationManager authenticationManager;
@Autowired private TokenStore tokenStore;
@Autowired(required = false) private List<TokenEnhancer> tokenEnhancer;
@Autowired private IValidateCodeService validateCodeService;
@Autowired private RandomValueAuthorizationCodeServices authorizationCodeServices;
private boolean reuseRefreshToken = true;
private AuthorizationServerTokenServices tokenServices;
private TokenGranter tokenGranter;
@Autowired private UserService userService;
/** * 是否登录同应用同账号互踢 */ @Value("${zlt.uaa.isSingleLogin:false}") private boolean isSingleLogin;
/** * 授权模式 */ @Bean public TokenGranter tokenGranter() { if (tokenGranter == null) { tokenGranter = new TokenGranter() { private CompositeTokenGranter delegate;
@Override public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) { if (delegate == null) { delegate = new CompositeTokenGranter(getAllTokenGranters()); } return delegate.grant(grantType, tokenRequest); } }; } return tokenGranter; }
/** * 所有授权模式:默认的5种模式 + 自定义的模式 */ private List<TokenGranter> getAllTokenGranters() { AuthorizationServerTokenServices tokenServices = tokenServices(); AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices(); OAuth2RequestFactory requestFactory = requestFactory(); //获取默认的授权模式 List<TokenGranter> tokenGranters = getDefaultTokenGranters(tokenServices, authorizationCodeServices, requestFactory); if (authenticationManager != null) { //添加手机号加验证码 tokenGranters.add(new MobileCodeGranter(authenticationManager,tokenServices,clientDetailsService,requestFactory, validateCodeService)); // 添加密码加图形验证码模式 tokenGranters.add(new PwdImgCodeGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory, validateCodeService,userService)); // 添加openId模式 tokenGranters.add(new OpenIdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); // 添加手机号加密码授权模式 tokenGranters.add(new MobilePwdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));
tokenGranters.add(new PwdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory,userService)); } return tokenGranters; }
/** * 默认的授权模式 */ private List<TokenGranter> getDefaultTokenGranters(AuthorizationServerTokenServices tokenServices , AuthorizationCodeServices authorizationCodeServices, OAuth2RequestFactory requestFactory) { List<TokenGranter> tokenGranters = new ArrayList<>(); // 添加授权码模式 tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory)); // 添加刷新令牌的模式 tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory)); // 添加隐士授权模式 tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory)); // 添加客户端模式 tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory)); if (authenticationManager != null) { // 添加密码模式 tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); } return tokenGranters; }
private AuthorizationServerTokenServices tokenServices() { if (tokenServices != null) { return tokenServices; } this.tokenServices = createDefaultTokenServices(); return tokenServices; }
private AuthorizationCodeServices authorizationCodeServices() { if (authorizationCodeServices == null) { authorizationCodeServices = new InMemoryAuthorizationCodeServices(); } return authorizationCodeServices; }
private OAuth2RequestFactory requestFactory() { return new DefaultOAuth2RequestFactory(clientDetailsService); }
private DefaultTokenServices createDefaultTokenServices() { DefaultTokenServices tokenServices = new CustomTokenServices(isSingleLogin); tokenServices.setTokenStore(tokenStore); tokenServices.setSupportRefreshToken(true); tokenServices.setReuseRefreshToken(reuseRefreshToken); tokenServices.setClientDetailsService(clientDetailsService); tokenServices.setTokenEnhancer(tokenEnhancer()); addUserDetailsService(tokenServices, this.userDetailsService); return tokenServices; }
private TokenEnhancer tokenEnhancer() { if (tokenEnhancer != null) { TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers(tokenEnhancer); return tokenEnhancerChain; } return null; }
private void addUserDetailsService(DefaultTokenServices tokenServices, UserDetailsService userDetailsService) { if (userDetailsService != null) { PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService)); tokenServices.setAuthenticationManager(new ProviderManager(Collections.singletonList(provider))); } } } |
CustomWebSecurityConfigurerAdapter,配置请求过滤路径等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
@Configuration //@EnableWebSecurity public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @Autowired private MemoryCacheSecurityContextRepository memoryCacheSecurityContextRepository; @Autowired private CustomAuthenticatedSessionStrategy customAuthenticatedSessionStrategy; @Autowired private CustomLogoutHandler customLogoutHandler; @Autowired private CustomAuthenticationFailureHandler customAuthenticationFailureHandler; @Autowired private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
@Override protected void configure(HttpSecurity http) throws Exception { http // .requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests() // .anyRequest().hasRole("ENDPOINT_ADMIN") // .and() .authorizeRequests() .antMatchers("/api/public/**") .permitAll().and().authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").and() .authorizeRequests().antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')").and() .authorizeRequests().anyRequest().authenticated().and().formLogin() .defaultSuccessUrl("/newweb/templates/admin_grid.html", true) // .loginPage("/login") .failureHandler(customAuthenticationFailureHandler).and().logout() // .logoutUrl("/logout") // .logoutSuccessUrl("/newweb/templates/admin_login.html") // .logoutSuccessHandler(new CustomLogoutSuccessHandler()) .invalidateHttpSession(true).addLogoutHandler(customLogoutHandler) .deleteCookies(SecurityConstants.SECURITY_TOKEN_KEY).and();
// 不创建session http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 自定义安全上下文仓库,覆盖默认的httpsession实现 http.securityContext().securityContextRepository(memoryCacheSecurityContextRepository); // 认证成功之后,不进行http session相关处理 http.sessionManagement().sessionAuthenticationStrategy(customAuthenticatedSessionStrategy); http.csrf().disable(); // http.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint); }
protected void configure(AuthenticationManagerBuilder auth) throws Exception { super.configure(auth); } } |
做一些验证码处理和账号密码验证等操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
@RestController @RequestMapping("/api/oauth") public class LoginController {
@Autowired private UserService userService; @Autowired private TokenEndpoint tokenEndpoint; @Autowired private LoginService loginService; @Autowired private UserProvider userProvider; @Autowired private ConfigValueUtil configValueUtil; @Autowired private RedisUtil redisUtil; @Autowired private ExpertInfoService expertInfoService; @Autowired private RoleService roleService;
@ApiOperation("登陆(切换登录模式需请清空loginForm中的值)") @PostMapping("/Login") public ActionResult<LoginVO> login(Principal principal, @RequestParam Map<String, String> parameters, @RequestBody LoginForm loginForm) throws LoginException { TenantContextHolder.clear(); UserInfo userInfo = new UserInfo(); String phone = loginForm.getPhone(); String phoneCode = loginForm.getPhoneCode(); String timestampkey = loginForm.getTimestamp(); if(StringUtil.isNotEmpty(phone)){ List<UserEntity> userEntityList = userService.list(new QueryWrapper<UserEntity>().lambda().eq(UserEntity::getMobilePhone,phone)); if(CollectionUtils.isNotEmpty(userEntityList)){ String phoneCode1 = String.valueOf(redisUtil.getString(phone)); if("null".equals(phoneCode1)){ throw new LoginException("验证码已过期!"); } if(!(phoneCode1.equals(phoneCode))){ throw new LoginException("验证码输入错误!"); } if(StringUtil.isNotEmpty(loginForm.getAccount())){ userEntityList = userEntityList.stream().filter(t->loginForm.getAccount().equals(t.getAccount())).collect(Collectors.toList()); } if(userEntityList.size() > 1){ List<UserLoginForm> userLoginFormList = JsonUtil.getJsonToList(userEntityList,UserLoginForm.class); LoginVO loginVO = new LoginVO(); loginVO.setUserLogFormList(userLoginFormList); return ActionResult.success(loginVO); } UserEntity userEntity = userEntityList.get(0); loginForm.setAccount(userEntity.getAccount()); loginForm.setPassword(userEntity.getPassword()); redisUtil.remove(phone); } }else{ String code = loginForm.getCode(); String timestamp = String.valueOf(redisUtil.getString(timestampkey)); if("null".equals(timestamp)){ throw new LoginException("验证码已过期!"); } if(!(code).equalsIgnoreCase(timestamp)){ throw new LoginException("验证码错误!"); } }
loginService.isExistUser(loginForm.getAccount().trim(), loginForm.getPassword().trim());
List<UserEntity> userEntityList = userService.getUserEntitys(StringUtil.isNotEmpty( loginForm.getPhonePassword())?loginForm.getPhonePassword():loginForm.getAccount()); UserEntity entity = new UserEntity(); if(userEntityList.size() > 1){ for (UserEntity item : userEntityList) { if(item.getPassword().equals(Md5Util.getStringMd5(loginForm.getPassword() + item.getSecretkey().toLowerCase()))){ if(StringUtil.isNotEmpty(loginForm.getPhonePassword())){ entity = userEntityList.stream().filter(t->loginForm.getAccount().equals(t.getAccount())).collect(Collectors.toList()).get(0); loginForm.setAccount(entity.getAccount()); loginForm.setPassword(entity.getPassword()); }else{ List<UserLoginForm> userLoginFormList = JsonUtil.getJsonToList(userEntityList,UserLoginForm.class); LoginVO loginVO = new LoginVO(); loginVO.setUserLogFormList(userLoginFormList); return ActionResult.success(loginVO); } } } if(StringUtil.isEmpty(loginForm.getPhonePassword())){ throw new LoginException("账号密码错误"); } } if(StringUtil.isEmpty(loginForm.getPhonePassword())){ entity = userEntityList.get(0); }
userInfo = loginService.userInfo(userInfo, entity);
// if(StringUtil.isNotEmpty(loginForm.getRoleId())){ // String[] roles = new String[1]; // roles[0] = loginForm.getRoleId(); // userInfo.setRoleIds(roles); // } // // List<RoleLoginVo> roleLoginVoList = new ArrayList<>(); // // if(ArrayUtils.isNotEmpty(userInfo.getRoleIds())){ // if(userInfo.getRoleIds().length > 1){ // for (String roleId : userInfo.getRoleIds()) { // RoleLoginVo roleLoginVo = JsonUtil.getJsonToBean(roleService.getById(roleId),RoleLoginVo.class); // roleLoginVoList.add(roleLoginVo); // } // } // } // if(CollectionUtil.isNotEmpty(roleLoginVoList)){ // LoginVO loginVO = new LoginVO(); // loginVO.setRoleList(roleLoginVoList); // return ActionResult.success(loginVO); // }
userInfo.setMybatisTenantId(entity.getTenantId()); ExpertInfoEntity expertInfoEntity = expertInfoService.getOne( new QueryWrapper<ExpertInfoEntity>().lambda().eq(ExpertInfoEntity::getUserId,entity.getId())); if(null != expertInfoEntity){ userInfo.setExpertId(expertInfoEntity.getId()); } //写入会话 userProvider.add(userInfo); //验证账号密码 Map<String, String> map = new HashMap<>(16); map.put("account",loginForm.getAccount()); map.put("password",loginForm.getPassword()); map.putAll(parameters); map.put("username", loginForm.getAccount()); OAuth2AccessToken oAuth2AccessToken; try { oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, map).getBody(); } catch (HttpRequestMethodNotSupportedException e) { throw new LoginException("账号密码错误"); }
TenantContextHolder.setTenant(entity.getTenantId());
//登陆日志记录在JwtTokenEnhancer类中 //获取主题 LoginVO loginVO = new LoginVO(); loginVO.setToken(oAuth2AccessToken.getTokenType() + " " + oAuth2AccessToken.getValue()); loginVO.setTheme(entity.getTheme() == null ? "classic" : entity.getTheme()); return ActionResult.success(loginVO); }
} |
返回token