Spring整合Oauth2单机版认证授权详情
Spring整合Oauth2单机版认证授权详情
一、概述
OAuth 2.0 规范定义了一个授权(delegation)协议,对于使用Web的应用程序和API在网络上传递授权决策非常有用。OAuth被用在各钟各样的应用程序中,包括提供用户认证的机制。
四种模式:
- 密码模式;
- 授权码模式;
- 简化模式;
- 客户端模式;
四种角色:
- 资源拥有者;
- 资源服务器;
- 第三方应用客户端;
- 授权服务器;
本文主要说明授权码模式。
首发地址:
品茗IT提供在线支持:
如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以加入我们的java学习圈,点击即可加入,共同学习,节约学习时间,减少很多在学习中遇到的难题。
二、环境配置
本文假设你已经引入Spring必备的一切了,已经是个Spring项目了,如果不会搭建,可以打开这篇文章看一看《Spring和Spring Mvc 5整合详解》。
本篇需要使用mybatis获取用户信息,不会使用mybatis的可以查看《SpringBoot入门建站全系列(三)Mybatis操作数据库》 。
2.1 maven依赖
下面的xml依赖只是oauth2作为jar包用到的依赖,如果要在web项目中启动,需要将下面依赖复制到启动项目中。
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.pomit</groupId>
<artifactId>SpringWork</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>Oauth</artifactId>
<packaging>jar</packaging>
<name>V</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>Oauth</finalName>
</build>
</project>
父模块可以在https://www.pomit.cn/spring/SpringWork/pom.xml获取。
2.2 Spring配置
配置文件分为两部分,一部分Mybatis的配置,一部分是Oauth2的配置。
2.2.1 Mybatis配置
spring-mybatis.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="annotationPropertyConfigurerMybatis"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>classpath:mybatis.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db.dirverClass}"></property>
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
<property name="initialSize" value="1" />
<property name="minIdle" value="1" />
<property name="maxTotal" value="20" />
<property name="validationQuery" value="SELECT 1" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- mybatis -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
<!-- 如果用xml方式,而不是用注解去写sql,这地方要注明xml文件的路径 -->
<!-- <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml" /> -->
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.pomit.springwork.mybatis.mapper" />
</bean>
</beans>
这里重点不是mybatis的配置,就不细说了。
配置文件:
db.url=jdbc:mysql://127.0.0.1:3306/cff?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
db.username=cff
db.password=123456
#db.dirverClass=com.mysql.cj.jdbc.Driver
#6.0以上用com.mysql.cj.jdbc.Driver.
db.dirverClass=com.mysql.jdbc.Driver
2.2.2 Oauth2配置
spring-oauth.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<context:component-scan base-package="cn.pomit.springwork">
</context:component-scan>
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore">
</bean>
<bean id="authorizationCodeServices"
class="cn.pomit.springwork.security.service.InMemoryAuthorizationCodeServices">
</bean>
<bean id="myClientDetailsService"
class="org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService">
<property name="clientDetailsStore">
<map>
<entry key="MwonYjDKBuPtLLlK" value-ref="clientDetails" />
<entry key="VJUpAlhdWPbvkpPy" value-ref="clientDetails1" />
</map>
</property>
</bean>
<bean id="clientDetails"
class="org.springframework.security.oauth2.provider.client.BaseClientDetails">
<property name="clientId" value="MwonYjDKBuPtLLlK" />
<property name="clientSecret" value="secret" />
<property name="accessTokenValiditySeconds" value="3600" />
<property name="refreshTokenValiditySeconds" value="3600" />
<property name="scope">
<set>
<value>read</value>
<value>write</value>
<value>trust</value>
</set>
</property>
<property name="authorizedGrantTypes">
<set>
<value>password</value>
<value>authorization_code</value>
<value>refresh_token</value>
<value>implicit</value>
</set>
</property>
<property name="authorities">
<list>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthority">
<constructor-arg type="java.lang.String" value="ROLE_CLIENT"></constructor-arg>
</bean>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthority">
<constructor-arg type="java.lang.String" value="ROLE_TRUSTED_CLIENT"></constructor-arg>
</bean>
</list>
</property>
</bean>
<bean id="clientDetails1"
class="org.springframework.security.oauth2.provider.client.BaseClientDetails">
<property name="clientId" value="VJUpAlhdWPbvkpPy" />
<property name="clientSecret" value="secret" />
<property name="accessTokenValiditySeconds" value="3600" />
<property name="refreshTokenValiditySeconds" value="3600" />
<property name="scope">
<set>
<value>read</value>
<value>write</value>
<value>trust</value>
</set>
</property>
<property name="authorizedGrantTypes">
<set>
<value>password</value>
<value>authorization_code</value>
<value>refresh_token</value>
<value>implicit</value>
</set>
</property>
<property name="authorities">
<list>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthority">
<constructor-arg type="java.lang.String" value="ROLE_CLIENT"></constructor-arg>
</bean>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthority">
<constructor-arg type="java.lang.String" value="ROLE_TRUSTED_CLIENT"></constructor-arg>
</bean>
</list>
</property>
</bean>
</beans>
这里:
-
tokenStore, 存储token使用的bean。
-
authorizationCodeServices,可以不必自己定义,这里是为了自定义token的长度。
-
myClientDetailsService,定义了两个client。
-
clientDetails,client的配置,分别包含clientId、密钥、token时长、刷新token时长、授权类型。
三、配置资源服务器
资源服务器需要使用@EnableResourceServer开启,是标明哪些资源是受Ouath2保护的。下面的代码标明/api是受保护的,而且资源id是my_rest_api。
ResourceServerConfiguration:
package cn.pomit.springwork.security.config;
import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
@Autowired
private TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.tokenStore(tokenStore).resourceId(RESOURCE_ID).stateless(false);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable().requestMatchers().antMatchers("/api/**").and().authorizeRequests()
.antMatchers("/api/**").authenticated().and().exceptionHandling()
.accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
四、配置授权服务器
授权服务器需要使用@EnableAuthorizationServer注解开启,主要负责client的认证,token的生成的。AuthorizationServerConfiguration需要依赖SpringSecurity的配置,因此下面还是要说SpringSecurity的配置。
AuthorizationServerConfiguration:
package cn.pomit.springwork.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
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.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static String REALM = "MY_OAUTH_REALM";
@Autowired
private TokenStore tokenStore;
@Autowired
@Qualifier("myClientDetailsService")
private ClientDetailsService clientDetailsService;
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
@Qualifier("authorizationCodeServices")
private AuthorizationCodeServices authorizationCodeServices;
@Bean
public PasswordEncoder noOpPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler(tokenStore))
.authenticationManager(authenticationManager);
endpoints.authorizationCodeServices(authorizationCodeServices);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients();
oauthServer.realm(REALM + "/client");
}
@Bean
@Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
@Bean
@Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
这里的
- clientDetailsService是配置文件中配置的客户端详情;
- tokenStore是token存储bean;
- authenticationManager安全管理器,使用Spring定义的即可,但要声明为bean。
- authorizationCodeServices是配置文件中我们自定义的token生成bean。
- PasswordEncoder密码处理工具类,必须指定的一个bean,可以使用NoOpPasswordEncoder来表明并未对密码做任何处理,实际上你可以实现PasswordEncoder来写自己的密码处理方案。
- UserApprovalHandler 需要定义为bean的Oauth2授权通过处理器。
五、SpringSecurity配置
5.1 SpringSecurity常规配置
SpringSecurity的配置在客户端和认证授权服务器中是必须的,这里是单机,它更是必须的。
OAuth2SecurityConfiguration:
package cn.pomit.springwork.security.config;
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.web.authentication.SimpleUrlAuthenticationFailureHandler;
import cn.pomit.springwork.security.ajax.AjaxAuthFailHandler;
import cn.pomit.springwork.security.ajax.AjaxAuthSuccessHandler;
import cn.pomit.springwork.security.ajax.AjaxLogoutSuccessHandler;
import cn.pomit.springwork.security.ajax.UnauthorizedEntryPoint;
import cn.pomit.springwork.security.service.OauthAuthenticationProvider;
@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private OauthAuthenticationProvider oauthAuthenticationProvider;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(oauthAuthenticationProvider);
}
@Override
public void configure(HttpSecurity http) throws Exception {
SimpleUrlAuthenticationFailureHandler hander = new SimpleUrlAuthenticationFailureHandler();
hander.setUseForward(true);
hander.setDefaultFailureUrl("/authLogin.html");
http.exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint()).and().csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/authLogin.html").permitAll()
.antMatchers("/oauth/**").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/authLogin.html")
.usernameParameter("userName")
.passwordParameter("userPwd")
.loginProcessingUrl("/login")
.successHandler(new AjaxAuthSuccessHandler()).failureHandler(new AjaxAuthFailHandler())
.and().logout().logoutUrl("/logout").logoutSuccessHandler(new AjaxLogoutSuccessHandler());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
这里,
-
OauthAuthenticationProvider 是用户名密码认证处理器。下面会说。
-
authenticationManager安全管理器,使用Spring定义的即可,但要声明为bean。
-
configure方法是SpringSecurity配置的常规写法。
-
UnauthorizedEntryPoint:未授权的统一处理方式。
-
successHandler、failureHandler、logoutSuccessHandler顾名思义,就是响应处理逻辑,如果不打算单独处理,只做跳转,有响应的successForwardUrl、failureForwardUrl、logoutSuccessUrl等。
5.2 配置Oauth2全局方法拦截
MethodSecurityConfig:
package cn.pomit.springwork.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Autowired
private OAuth2SecurityConfiguration securityConfig;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
5.3 SpringSecurity用户名密码验证器
OauthAuthenticationProvider 实现了AuthenticationProvider 接口的authenticate方法,提供用户名密码的校验,校验成功后,生成UsernamePasswordAuthenticationToken。
这里面用到的OauthUserDetailsService,需要自己实现从数据库中获取。
OauthAuthenticationProvider :
package cn.pomit.springwork.security.service;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import cn.pomit.springwork.security.detail.OauthUserDetails;
@Component
public class OauthAuthenticationProvider implements AuthenticationProvider {
@Autowired
private OauthUserDetailsService oauthUserDetailsService;
/**
* 自定义验证方式
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 用户名密码校验
OauthUserDetails oauthUserDetails = (OauthUserDetails) oauthUserDetailsService
.loadUserByUsername(authentication.getName());
System.out.println(authentication.getName() + "+++++++++++++++++" + authentication.getCredentials());
if (!oauthUserDetails.getUserName().equals(authentication.getName())
|| !oauthUserDetails.getPassword().equals(authentication.getCredentials())) {
throw new BadCredentialsException("用户名或密码错误。");
}
Collection<? extends GrantedAuthority> authorities = oauthUserDetails.getAuthorities();
return new UsernamePasswordAuthenticationToken(oauthUserDetails.getUsername(), oauthUserDetails.getPassword(),
authorities);
}
@Override
public boolean supports(Class<?> arg0) {
return true;
}
}
5.4 用户验证信息获取
OauthUserDetailsService:
package cn.pomit.springwork.security.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import cn.pomit.springwork.mybatis.domain.UserInfo;
import cn.pomit.springwork.mybatis.service.UserInfoService;
import cn.pomit.springwork.security.detail.OauthUserDetails;
@Service("oauthUserDetailsService")
public class OauthUserDetailsService implements UserDetailsService {
@Autowired
private UserInfoService appUserService;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
UserInfo user;
try {
user = appUserService.getUserInfoByUserName(userName);
} catch (Exception e) {
throw new UsernameNotFoundException("user select fail");
}
if (user == null) {
throw new UsernameNotFoundException("no user found");
} else {
try {
return new OauthUserDetails(user);
} catch (Exception e) {
throw new UsernameNotFoundException("user role select fail");
}
}
}
}
OauthUserDetails:
package cn.pomit.springwork.security.detail;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import cn.pomit.springwork.mybatis.domain.UserInfo;
public class OauthUserDetails extends UserInfo implements UserDetails {
public OauthUserDetails(UserInfo appUser) {
super(appUser);
}
/**
*
*/
private static final long serialVersionUID = 6272869114201567325L;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.createAuthorityList("USER");
}
@Override
public String getUsername() {
return super.getUserName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getPassword() {
return super.getPasswd();
}
}
六、自定义的Token的Code生成逻辑
这个就是定义在配置文件中的authorizationCodeServices。
InMemoryAuthorizationCodeServices:
package cn.pomit.springwork.security.service;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
public class InMemoryAuthorizationCodeServices extends RandomValueAuthorizationCodeServices {
protected final ConcurrentHashMap<String, OAuth2Authentication> authorizationCodeStore = new ConcurrentHashMap<String, OAuth2Authentication>();
private RandomValueStringGenerator generator = new RandomValueStringGenerator(16);
@Override
protected void store(String code, OAuth2Authentication authentication) {
this.authorizationCodeStore.put(code, authentication);
}
@Override
public OAuth2Authentication remove(String code) {
OAuth2Authentication auth = this.authorizationCodeStore.remove(code);
return auth;
}
@Override
public String createAuthorizationCode(OAuth2Authentication authentication) {
String code = generator.generate();
store(code, authentication);
return code;
}
}
七、测试Oauth2
7.1 Web测试接口
OauthRest :
package cn.pomit.springwork.security.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import cn.pomit.springwork.security.ajax.ResultModel;
@RestController
@RequestMapping("/")
public class OauthRest {
@RequestMapping(value = "/api/test", method = { RequestMethod.GET })
public ResultModel api() {
return ResultModel.ok("测试api成功");
}
@RequestMapping(value = "/user/test", method = { RequestMethod.GET })
public ResultModel user() {
return ResultModel.ok("测试user成功");
}
}
7.2 测试过程
- 首先访问http://127.0.0.1:8080/api/test。 提示
<oauth>
<error_description>
An Authentication object was not found in the SecurityContext
</error_description>
<error>unauthorized</error>
</oauth>
这标明,oauth2控制了资源,需要access_token。
- 请求授权接口oauth/authorize:
http://localhost:8080/oauth/authorize?response_type=code&client_id=MwonYjDKBuPtLLlK&redirect_uri=http://www.pomit.cn
client_id是配置文件中写的,redirect_uri随意,因为单机版的只是测试接口。后面会整理下多机版的博文。
打开后自动调整到登录页面:
在这里插入图片描述输入正确的用户名密码,调整到授权页面。
在这里插入图片描述点击同意以后,调整到redirect_uri指定的页面。
在这里插入图片描述获取到code:TnSFA6vrIZiKadwr
- 用code换取access_token,请求token接口:
http://127.0.0.1:8080/oauth/token?grant_type=authorization_code&code=TnSFA6vrIZiKadwr&client_id=MwonYjDKBuPtLLlK&client_secret=secret&redirect_uri=http://www.pomit.cn
。这个请求必须是post,因此不能在浏览器中输入了,可以使用postman:
获取到access_token为:686dc5d5-60e9-48af-bba7-7f16b49c248b。
- 用access_token请求/api/test接口:
http://127.0.0.1:8080/api/test?access_token=686dc5d5-60e9-48af-bba7-7f16b49c248b
结果为:
八、过程中用到的其他实体及逻辑
AjaxAuthFailHandler:
AjaxAuthSuccessHandler:
AjaxLogoutSuccessHandler:
UnauthorizedEntryPoint:
ResultModel:
九、Mybatis的逻辑及实体
UserInfoService:
UserInfoMapper:
UserInfo:
详细完整的代码,可以访问品茗IT-博客《Spring整合Oauth2单机版认证授权详情》进行查看
品茗IT-博客专题:https://www.pomit.cn/lecture.html汇总了Spring专题、Springboot专题、SpringCloud专题、web基础配置专题。
快速构建项目
喜欢这篇文章么,喜欢就加入我们一起讨论Spring技术吧!
品茗IT交流群