4、
总之坑很多,我们只有一步一个坑慢慢的前行去填坑!
5、
6、核心代码:
package com.hbasesoft.vcc.sgp.ability.oauth.server.config;
import com.hbasesoft.framework.db.core.config.DbParam;
import com.hbasesoft.framework.db.core.utils.DataSourceUtil;
import com.hbasesoft.vcc.sgp.ability.oauth.server.security.MyBCryptPasswordEncoder;
import com.hbasesoft.vcc.sgp.ability.oauth.server.security.MyRedisTokenStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
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.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import javax.sql.DataSource;
/**
* @Author: fb
* @Description client信息通过jdbc去查询数据库验证,(注释部分是通过Redis方法去验证)
* @Date: Create in 14:54 2018/2/1
* @Modified By
*/
@Configuration
@EnableAuthorizationServer
public class OAuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
//本系统获得dataSource的方式
private DataSource dataSource = DataSourceUtil.registDataSource("master", new DbParam("master"));
@Autowired
private RedisConnectionFactory connectionFactory;
@Bean
public PasswordEncoder passwordEncoder() {
return new MyBCryptPasswordEncoder();
}
/*
@Autowired
private ClientDetailsService clientDetailsService;
*/
@Bean
public MyRedisTokenStore tokenStore() {
return new MyRedisTokenStore(connectionFactory);
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
@Bean
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
@Bean
public ApprovalStore approvalStore() {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore());
return store;
}
/*
@Bean
public UserApprovalHandler userApprovalHandler() throws Exception {
MyUserApprovalHandler handler = new MyUserApprovalHandler();
handler.setApprovalStore(approvalStore());
handler.setClientDetailsService(clientDetailsService);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(
clientDetailsService));
handler.setUseApprovalStore(true);
return handler;
}
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// //客户端信息通过Redis去取得验证
// final RedisClientDetailsServiceBuilder builder = new RedisClientDetailsServiceBuilder();
// clients.setBuilder(builder);
//通过JDBC去查询数据库oauth_client_details表验证clientId信息
clients.jdbc(this.dataSource).clients(this.clientDetailsService());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.tokenStore(this.tokenStore())
// .userDetailsService(userDetailsService)
// .userApprovalHandler(userApprovalHandler())
.authorizationCodeServices(this.authorizationCodeServices());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
}
以上代码的dataSource是我们框架系统封装好的,如果你们dataSource没有封装好,则直接用
@Autowired
private DataSource dataSource;
MyBCryptPasswordEncoder类是我自定义的一个类,用来重新match方法。
package com.hbasesoft.vcc.sgp.ability.oauth.server.security;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
*
*
* @author fb
* @version 1.0
* @taskId
* @CreateDate Create in 13:50 2018/2/12
* @see com.hbasesoft.vcc.sgp.ability.oauth.server.security
* @since V1.0
*/
public class MyBCryptPasswordEncoder extends BCryptPasswordEncoder {
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
String presentedPassword =passwordEncoder.encode(encodedPassword);
return passwordEncoder.matches(rawPassword, presentedPassword);
}
}
上面的类中,client信息采用的是存储在数据库中,而token信息,则采用redis来存储,redis用RedisTokenStore来实现,但是由于在Spring的5.0中会出现如下错误:
我将我的spring boot项目版本升到2.0.0.M7后,集成了spring security oauth2(默认版本),redis(默认版本),并且用redis来存储token。项目正常启动后,请求token时报错
nested exception is java.lang.NoSuchMethodError: org.springframework.data.redis.connection.RedisConnection.set([B[B)V
看到报错信息,我的第一反应是版本冲突,但是我的依赖的都是spring-boot-starter-parent中的默认版本。 pring-data-redis 2.0版本中set(String,String)被弃用了。然后我按照网页中的决解方法“spring-date-redis”改为2.0.1.RELEASE和”jedis“为2.9.0(显式声明),结果还是报一样的错。用了一个极端的方式解决的。spring-data-redis 2.0版本中set(String,String)被弃用了,要使用RedisConnection.stringCommands().set(…),所有我自定义一个RedisTokenStore,代码和RedisTokenStore一样,只是把所有conn.set(…)都换成conn..stringCommands().set(…),测试后方法可行。
MyRedisTokenStore.java类代码如下:
package com.hbasesoft.vcc.sgp.ability.oauth.server.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* 重写tokenStore .因为最新版中RedisTokenStore的set已经被弃用了,
* 所以我就只能自定义一个,代码和RedisTokenStore一样,
* 只是把所有conn.set(…)都换成conn..stringCommands().set(…),
*
*
* @author fb
* @version 1.0
* @taskId
* @CreateDate Create in 10:59 2018/2/13
* @see com.hbasesoft.vcc.sgp.ability.oauth.server.security
* @since V1.0
*/
@Component
public class MyRedisTokenStore implements TokenStore {
private static final String ACCESS = "access:";
private static final String AUTH_TO_ACCESS = "auth_to_access:";
private static final String AUTH = "auth:";
private static final String REFRESH_AUTH = "refresh_auth:";
private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
private static final String REFRESH = "refresh:";
private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
private static final String UNAME_TO_ACCESS = "uname_to_access:";
private final RedisConnectionFactory connectionFactory;
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
private String prefix = "";
public MyRedisTokenStore(RedisConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
this.authenticationKeyGenerator = authenticationKeyGenerator;
}
public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) {
this.serializationStrategy = serializationStrategy;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
private RedisConnection getConnection() {
return this.connectionFactory.getConnection();
}
private byte[] serialize(Object object) {
return this.serializationStrategy.serialize(object);
}
private byte[] serializeKey(String object) {
return this.serialize(this.prefix + object);
}
private OAuth2AccessToken deserializeAccessToken(byte[] bytes) {
return (OAuth2AccessToken)this.serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);
}
private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
return (OAuth2Authentication)this.serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
}
private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) {
return (OAuth2RefreshToken)this.serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class);
}
private byte[] serialize(String string) {
return this.serializationStrategy.serialize(string);
}
private String deserializeString(byte[] bytes) {
return this.serializationStrategy.deserializeString(bytes);
}
@Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
String key = this.authenticationKeyGenerator.extractKey(authentication);
byte[] serializedKey = this.serializeKey(AUTH_TO_ACCESS + key);
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(serializedKey);
} finally {
conn.close();
}
OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
if (accessToken != null) {
OAuth2Authentication storedAuthentication = this.readAuthentication(accessToken.getValue());
if (storedAuthentication == null || !key.equals(this.authenticationKeyGenerator.extractKey(storedAuthentication))) {
this.storeAccessToken(accessToken, authentication);
}
}
return accessToken;
}
@Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
return this.readAuthentication(token.getValue());
}
@Override
public OAuth2Authentication readAuthentication(String token) {
byte[] bytes = null;
RedisConnection conn = this.getConnection();
try {
bytes = conn.get(this.serializeKey("auth:" + token));
} finally {
conn.close();
}
OAuth2Authentication auth = this.deserializeAuthentication(bytes);
return auth;
}
@Override
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return this.readAuthenticationForRefreshToken(token.getValue());
}
public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
RedisConnection conn = getConnection();
try {
byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token));
OAuth2Authentication auth = deserializeAuthentication(bytes);
return auth;
} finally {
conn.close();
}
}
@Override
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
byte[] serializedAccessToken = serialize(token);
byte[] serializedAuth = serialize(authentication);
byte[] accessKey = serializeKey(ACCESS + token.getValue());
byte[] authKey = serializeKey(AUTH + token.getValue());
byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication));
byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
RedisConnection conn = getConnection();
try {
conn.openPipeline();
conn.stringCommands().set(accessKey, serializedAccessToken);
conn.stringCommands().set(authKey, serializedAuth);
conn.stringCommands().set(authToAccessKey, serializedAccessToken);
if (!authentication.isClientOnly()) {
conn.rPush(approvalKey, serializedAccessToken);
}
conn.rPush(clientId, serializedAccessToken);
if (token.getExpiration() != null) {
int seconds = token.getExpiresIn();
conn.expire(accessKey, seconds);
conn.expire(authKey, seconds);
conn.expire(authToAccessKey, seconds);
conn.expire(clientId, seconds);
conn.expire(approvalKey, seconds);
}
OAuth2RefreshToken refreshToken = token.getRefreshToken();
if (refreshToken != null && refreshToken.getValue() != null) {
byte[] refresh = serialize(token.getRefreshToken().getValue());
byte[] auth = serialize(token.getValue());
byte[] refreshToAccessKey = serializeKey(REFRESH_TO_ACCESS + token.getRefreshToken().getValue());
conn.stringCommands().set(refreshToAccessKey, auth);
byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + token.getValue());
conn.stringCommands().set(accessToRefreshKey, refresh);
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken;
Date expiration = expiringRefreshToken.getExpiration();
if (expiration != null) {
int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L)
.intValue();
conn.expire(refreshToAccessKey, seconds);
conn.expire(accessToRefreshKey, seconds);
}
}
}
conn.closePipeline();
} finally {
conn.close();
}
}
private static String getApprovalKey(OAuth2Authentication authentication) {
String userName = authentication.getUserAuthentication() == null ? "": authentication.getUserAuthentication().getName();
return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
}
private static String getApprovalKey(String clientId, String userName) {
return clientId + (userName == null ? "" : ":" + userName);
}
@Override
public void removeAccessToken(OAuth2AccessToken accessToken) {
this.removeAccessToken(accessToken.getValue());
}
@Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
byte[] key = serializeKey(ACCESS + tokenValue);
byte[] bytes = null;
RedisConnection conn = getConnection();
try {
bytes = conn.get(key);
} finally {
conn.close();
}
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
return accessToken;
}
public void removeAccessToken(String tokenValue) {
byte[] accessKey = serializeKey(ACCESS + tokenValue);
byte[] authKey = serializeKey(AUTH + tokenValue);
byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue);
RedisConnection conn = getConnection();
try {
conn.openPipeline();
conn.get(accessKey);
conn.get(authKey);
conn.del(accessKey);
conn.del(accessToRefreshKey);
// Don't remove the refresh token - it's up to the caller to do that
conn.del(authKey);
List
认证类:
package com.hbasesoft.vcc.sgp.ability.oauth.server.config;
import org.springframework.context.annotation.Configuration;
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;
/**
* @Author: fb
* @Description 访问权限配置
* order = 3的过滤器链
* @Date: Create in 10:36 2018/2/1
* @Modified By
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatchers().antMatchers("/api/**").and().authorizeRequests()
.anyRequest().authenticated();
}
}
package com.hbasesoft.vcc.sgp.ability.oauth.server.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
* @Author: fb
* @Description 配置页面视图
* (在Spring5.0中,WebMvcConfigurerAdapter已经弃用,替代类:WebMvcConfigurationSupport或者DelegatingWebMvcConfiguration)
* extends WebMvcConfigurerAdapter+@EnableWebMvc 等同于 extends WebMvcConfigurationSupport
* 切勿使用@EnableWebMvc和 extends WebMvcConfigurationSupport 在一起
* @Date: Create in 9:36 2018/2/2
* @Modified By
*/
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.hbasesoft.vcc.sgp.ability.oauth.server"})
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("authorize");
//配置授权确认页面视图
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/index").addResourceLocations("/index.html");
}
}
package com.hbasesoft.vcc.sgp.ability.oauth.server.config;
import com.hbasesoft.vcc.sgp.ability.oauth.server.security.AdminAuthenticationProvider;
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.method.configuration.EnableGlobalMethodSecurity;
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;
/**
* @Author: fb
* @Description 访问权限配置(URL的认证。可配置拦截什么URL,设置什么权限等安全限制)
* order = 0的过滤器链
* @Date: Create in 10:36 2018/2/1
* @Modified By
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Bean
public AdminAuthenticationProvider adminAuthenticationProvider() {
AdminAuthenticationProvider provider = new AdminAuthenticationProvider();
return provider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中创建了一个用户,该用户的名称为user,密码为password,用户角色为USER。
//auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
auth.authenticationProvider(this.adminAuthenticationProvider());
}
@Bean(name = "authenticationManager")
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//默认配置(所有认证请求都需要授权,要求用户在进入你的应用的任何URL之前都进行验证)
/*
http.authorizeRequests().anyRequest().authenticated().and()
.formLogin().and()
.httpBasic();
*/
//配置进入以下URL之前不需要验证
http.csrf().disable();
http.authorizeRequests().antMatchers( "/admin/login.html", "/admin/login",
"/agreement.html", "/error.html").permitAll();
//通过 formLogin() 定义当需要用户登录时候,转到的登录页面
// http.formLogin().loginPage("/login").permitAll().and().authorizeRequests().anyRequest().authenticated();
}
}
上面代码中,用户和密码的信息验证通过动态查询数据库来验证。不采用内存中写死。
所以自定义了AdminAuthenticationProvider 和AdminAuthenticationToken类。
package com.hbasesoft.vcc.sgp.ability.oauth.server.security;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.ArrayList;
import java.util.Collection;
public class AdminAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = -3625665688626567368L;
private Collection authorities = new ArrayList<>();
private Object principal;
private Object credentials;
private String username;
public AdminAuthenticationToken(String username, String password) {
super(null);
this.username = username;
this.credentials = password;
}
public void setPrincipal(Object principal) {
this.principal = principal;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public Object getCredentials() {
return this.credentials;
}
public String getUsername() {
return this.username;
}
@Override
public Collection getAuthorities() {
return this.authorities;
}
public void setAuthorities(
Collection extends GrantedAuthority> authorities) {
this.authorities.addAll(authorities);
}
}
package com.hbasesoft.vcc.sgp.ability.oauth.server.security;
import com.hbasesoft.framework.common.utils.logger.LoggerUtil;
import com.hbasesoft.vcc.sgp.ability.oauth.server.service.impl.UserDetailsImpl;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.persistence.Transient;
import java.util.List;
public class AdminAuthenticationProvider implements AuthenticationProvider {
;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
@Override
public boolean supports(Class> authentication) {
return authentication.equals(AdminAuthenticationToken.class);
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
AdminAuthenticationToken token = (AdminAuthenticationToken) authentication;
String username = token.getUsername();
String password = token.getCredentials().toString();
UserDetails loadedUser = null;
try {
loadedUser = new UserDetailsImpl(username,password,this.getGrantedAuthorityList());
} catch (UsernameNotFoundException notFound) {
throw notFound;
} catch (Exception repositoryProblem) {
throw new InternalAuthenticationServiceException(
repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
LoggerUtil.error("UserDetailsService returned null, which is an interface contract violation.");
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
try {
additionalAuthenticationChecks(loadedUser, token);
} catch (AuthenticationException exception) {
LoggerUtil.error("Username and password are invalid.", exception);
throw exception;
}
token.setPrincipal(loadedUser);
token.setDetails(loadedUser);
token.setAuthorities(loadedUser.getAuthorities());
authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(authentication);
return authentication;
}
protected void additionalAuthenticationChecks(UserDetails userDetails, AdminAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
LoggerUtil.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String authPassword = authentication.getCredentials().toString();
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
String presentedPassword =passwordEncoder.encode(authPassword);
if (!passwordEncoder.matches(userDetails.getPassword(),presentedPassword)) {
LoggerUtil.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Transient
public List getGrantedAuthorityList() {
return AuthorityUtils.createAuthorityList("ROLE_USER");
}
}
package com.hbasesoft.vcc.sgp.ability.oauth.server.service.impl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import javax.persistence.Transient;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
/**
*
*
* @author fb
* @version 1.0
* @taskId
* @CreateDate Create in 11:18 2018/2/11
* @see com.hbasesoft.vcc.sgp.ability.oauth.server.service.impl
* @since V1.0
*/
public class UserDetailsImpl extends User{
public UserDetailsImpl(String username, String password, Collection extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
}
在OAuth2.0授权认证中,生成code信息都需要登录授权才能访问。所以登录验证的代码如下:
package com.hbasesoft.vcc.sgp.ability.oauth.server.controller;
import com.hbasesoft.framework.common.utils.logger.LoggerUtil;
import com.hbasesoft.vcc.sgp.ability.oauth.server.api.LoginAccountClient;
import com.hbasesoft.vcc.sgp.ability.oauth.server.security.AdminAuthenticationToken;
import com.hbasesoft.vcc.sgp.common.controller.CommonConstant;
import com.hbasesoft.vcc.sgp.common.controller.ErrorCodeDef;
import com.hbasesoft.vcc.sgp.uum.user.pojo.DataJsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登录验证授权
*
* @author fb
* @version 1.0
* @taskId
* @CreateDate Create in 11:38 2018/2/9
* @see com.hbasesoft.vcc.sgp.ability.oauth.server.controller
* @since V1.0
*/
@RestController
@RequestMapping("/oauth")
public class AccountController extends BaseController{
@Autowired
private AuthenticationManager authenticationManager;
@Resource
private LoginAccountClient loginAccountClient;
@GetMapping(value = "/loginIn")
public String loginByAccount(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
String username = getParameter("username", ErrorCodeDef.LOGIN_USERNAME_IS_NULL);
String password = getParameter("password", ErrorCodeDef.LOGIN_PASSWORD_IS_NULL);
long startTime = System.currentTimeMillis();
LoggerUtil.info("begin login ....");
DataJsonResult result = loginAccountClient.loginVerifyByUserName(username,password);
if(result.getCode().equals(CommonConstant.ERROR)){
LoggerUtil.error(result.getMsg());
return "failed";
}
try {
AdminAuthenticationToken authRequest = new AdminAuthenticationToken(username,password);
Authentication authResult = this.authenticationManager.authenticate(authRequest);
long endTime = System.currentTimeMillis();
LoggerUtil.info("login successfully.");
LoggerUtil.info("AccountController.loginByAccount:"+ (endTime - startTime) / 1000 + "秒");
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.onAuthenticationSuccess(request, response,authResult);
return "success";
} catch (AuthenticationException ex) {
LoggerUtil.error("login failed.", ex);
return "error";
}
}
}
package com.hbasesoft.vcc.sgp.uum.user.pojo;
/**
* @Author: fb
* @Description 数据返回JSON格式
* @Date: Create in 13:43 2018/2/5
* @Modified By
*/
public class DataJsonResult {
private String code;//错误代码
private String msg;//错误信息描述
private String ret;//请求是否成功
private Object data;//返回的数据
public DataJsonResult(){
}
public DataJsonResult(String code, String msg, String ret, Object data) {
this.code = code;
this.msg = msg;
this.ret = ret;
this.data = data;
}
@Override
public String toString() {
return "DataJsonResult{" +
"code='" + code + '\'' +
", msg='" + msg + '\'' +
", ret='" + ret + '\'' +
", data=" + data +
'}';
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getRet() {
return ret;
}
public void setRet(String ret) {
this.ret = ret;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
LoginAccountClient这个类是调用其他系统的用户信息登录验证。如果你们不需要跨系统调接口,直接在方法中写验证方法就好,LoginAccountClient用到了FeignClient技术。
package com.hbasesoft.vcc.sgp.ability.oauth.server.api;
import com.hbasesoft.vcc.sgp.uum.user.api.LoginAccountRemoteService;
import com.hbasesoft.vcc.sgp.uum.user.pojo.DataJsonResult;
import com.hbasesoft.vcc.sgp.uum.user.pojo.UpmUserLoginRecord;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
*
*
* @author fb
* @version 1.0
* @taskId
* @CreateDate Create in 10:51 2018/2/11
* @see com.hbasesoft.vcc.sgp.ability.oauth.server.api
* @since V1.0
*/
@FeignClient(name="uum-user",url = "${uumServer.uum-url}")
public interface LoginAccountClient extends LoginAccountRemoteService{
@RequestMapping(value = "/loginController/loginIn",method = RequestMethod.GET)
@Override
DataJsonResult loginVerifyByUserName(@RequestParam("username") String username, @RequestParam("password")String password);
@RequestMapping(value = "/loginController/saveLoginRecord",method = RequestMethod.POST)
@Override
String saveloginRecord(@RequestParam("record") UpmUserLoginRecord record);
}
application.yml中的配置信息:
project: #项目信息
name: ABILITY-OAUTH2.0-SERVER
model: dev
server:
plat-component: BOOTSTRAP-PLAT-SERVER
plat-configuration: BOOTSTRAP-PLAT-SERVER
uum-user: BOOTSTRAP-UUM-SERVER
server: #系统配置
port: 18083
spring: #应用配置
application:
name: ${project.name}
uumServer:
uum-url: http://localhost:8083/
master: #主数据库配置
db:
type: mysql
url: jdbc:mysql://www.hbasesoft.com:3306/sgp_ability?useUnicode=true&characterEncoding=UTF-8&generateSimpleParameterMetadata=true
username: sgp
password: sgp
cache: #缓存配置
model: SIMPLE
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
timeout: 0
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
session:
store-type: none
eureka: #服务注册中心
instance:
hostname: 127.0.0.1
status-page-url: http://${eureka.instance.hostname}:${server.port}/swagger-ui.html
client:
serviceUrl:
defaultZone: http://127.0.0.1:8080/eureka/
主要的代码就是这样了。
有疑问,请联系,Spring5.0预计还有很多坑,网上资源较少,特记录下来。
源码到时候上到git中。
努力奋斗!