OAuth 2.0 规范定义了一个授权(delegation)协议,对于使用Web的应用程序和API在网络上传递授权决策非常有用。OAuth被用在各钟各样的应用程序中,包括提供用户认证的机制。
四种模式:
- 密码模式;
- 授权码模式;
- 简化模式;
- 客户端模式;四种角色:
- 资源拥有者;
- 资源服务器;
- 第三方应用客户端;
- 授权服务器;
本文主要说明授权码模式。
**如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以<a
href="https://jq.qq.com/?_wv=1027&k=52sgH1J"
target="_blank">
加入我们的java学习圈,点击即可加入
</a>
,共同学习,节约学习时间,减少很多在学习中遇到的难题。**
本文假设你已经引入Spring必备的一切了,已经是个Spring项目了,如果不会搭建,可以打开这篇文章看一看《Spring和Spring Mvc 5整合详解》。
本篇需要使用mybatis获取用户信息,不会使用mybatis的可以查看《SpringBoot入门建站全系列(三)Mybatis操作数据库》 。
下面的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获取。
配置文件分为两部分,一部分Mybatis的配置,一部分是Oauth2的配置。
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
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>
这里:
资源服务器需要使用@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;
}
}
这里的
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();
}
}
这里,
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();
}
}
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;
}
}
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();
}
}
这个就是定义在配置文件中的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;
}
}
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成功");
}
}
这标明,oauth2控制了资源,需要access_token。
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
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。
http://127.0.0.1:8080/api/test?access_token=686dc5d5-60e9-48af-bba7-7f16b49c248b
结果为:
在这里插入图片描述
八、过程中用到的其他实体及逻辑AjaxAuthFailHandler:
package cn.pomit.springwork.security.ajax;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
public class AjaxAuthFailHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
if (isAjaxRequest(request)) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
} else {
setDefaultFailureUrl("/authLogin.html");
super.onAuthenticationFailure(request, response, exception);
}
}
public static boolean isAjaxRequest(HttpServletRequest request) {
String ajaxFlag = request.getHeader("X-Requested-With");
return ajaxFlag != null && "XMLHttpRequest".equals(ajaxFlag);
}
}
AjaxAuthSuccessHandler:
```java
package cn.pomit.springwork.security.ajax;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
public class AjaxAuthSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
```javascript
protected final Log logger = LogFactory.getLog(this.getClass());
```javascript
@Override
```javascript
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
```javascript
Authentication authentication) throws IOException, ServletException {
```javascript
request.getSession().setAttribute("userName", authentication.getName());
```javascript
if (isAjaxRequest(request)) {
```javascript
ResultModel rm = new ResultModel("ok");
```javascript
ObjectMapper mapper = new ObjectMapper();
```javascript
response.setStatus(HttpStatus.OK.value());
```javascript
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
```javascript
mapper.writeValue(response.getWriter(), rm);
```javascript
} else {
```javascript
super.onAuthenticationSuccess(request, response, authentication);
```javascript
}
```javascript
}
```javascript
public static boolean isAjaxRequest(HttpServletRequest request) {
```javascript
String ajaxFlag = request.getHeader("X-Requested-With");
```javascript
return ajaxFlag != null && "XMLHttpRequest".equals(ajaxFlag);
```javascript
}
}
```javascript
AjaxLogoutSuccessHandler:
package cn.pomit.springwork.security.ajax;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
public class AjaxLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
if (isAjaxRequest(request)) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
super.onLogoutSuccess(request, response, authentication);
}
}
public static boolean isAjaxRequest(HttpServletRequest request) {
String ajaxFlag = request.getHeader("X-Requested-With");
return ajaxFlag != null && "XMLHttpRequest".equals(ajaxFlag);
}
}
UnauthorizedEntryPoint:
```java
package cn.pomit.springwork.security.ajax;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
```javascript
@Override
```javascript
public void commence(HttpServletRequest request, HttpServletResponse response,
```javascript
AuthenticationException authException) throws IOException, ServletException {
```javascript
if (isAjaxRequest(request)) {
```javascript
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
```javascript
} else {
```javascript
response.sendRedirect("/authLogin.html");
```javascript
}
```javascript
}
```javascript
public static boolean isAjaxRequest(HttpServletRequest request) {
```javascript
String ajaxFlag = request.getHeader("X-Requested-With");
```javascript
return ajaxFlag != null && "XMLHttpRequest".equals(ajaxFlag);
```javascript
}
}
```javascript
ResultModel:
package cn.pomit.springwork.security.ajax;
public class ResultModel {
String code;
String message;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public ResultModel() {
}
public ResultModel(String code, String messgae) {
this.code = code;
this.message = messgae;
}
public ResultModel(String messgae) {
this.code = "00000";
this.message = messgae;
}
public static ResultModel ok(String messgae) {
return new ResultModel("00000", messgae);
}
}
## 九、Mybatis的逻辑及实体
UserInfoService:
```java
package cn.pomit.springwork.mybatis.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.pomit.springwork.mybatis.domain.UserInfo;
import cn.pomit.springwork.mybatis.mapper.UserInfoMapper;
@Service
public class UserInfoService {
```javascript
@Autowired
```javascript
UserInfoMapper userInfoDao;
```javascript
public UserInfo getUserInfoByUserName(String userName){
```javascript
return userInfoDao.findByUserName(userName);
```javascript
}
}
```javascript
UserInfoMapper:
package cn.pomit.springwork.mybatis.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import cn.pomit.springwork.mybatis.domain.UserInfo;
@Mapper
public interface UserInfoMapper {
@Select({
"<script>",
"SELECT ",
"user_name as userName,passwd,name,mobile,valid,user_type as userType",
"FROM user_info",
"WHERE user_name = #{userName,jdbcType=VARCHAR}",
"</script>"})
UserInfo findByUserName(@Param("userName") String userName);
@Select({
"<script>",
"SELECT user_name as userName,passwd,name,mobile,valid,user_type as userType",
"FROM user_info",
"</script>"})
List<UserInfo> selectAll();
@Update({
"<script>",
" update user_info set",
" passwd = #{passwd, jdbcType=VARCHAR}",
" where user_name=#{userName}",
"</script>"
})
int update(@Param("userName") String userName, @Param("passwd") String passwd);
@Delete({
"<script>",
" delete from user_info",
" where user_name=#{userName}",
"</script>"
})
int delete(@Param("userName") String userName);
}
UserInfo:
```java
package cn.pomit.springwork.mybatis.domain;
public class UserInfo {
```javascript
private String userName;
```javascript
private String passwd;
```javascript
private String name;
```javascript
private String mobile;
```javascript
private String valid;
```javascript
private String userType;
```javascript
public UserInfo() {
```javascript
}
```javascript
public UserInfo(UserInfo src) {
```javascript
this.userName = src.userName;
```javascript
this.passwd = src.passwd;
```javascript
this.name = src.name;
```javascript
this.mobile = src.mobile;
```javascript
this.valid = src.valid;
```javascript
this.userType = src.userType;
```javascript
}
```javascript
public UserInfo(String userName, String passwd, String name, String mobile, String valid) {
```javascript
super();
```javascript
this.userName = userName;
```javascript
this.passwd = passwd;
```javascript
this.name = name;
```javascript
this.mobile = mobile;
```javascript
this.valid = valid;
```javascript
}
```javascript
public void setUserName(String userName) {
```javascript
this.userName = userName;
```javascript
}
```javascript
public String getUserName() {
```javascript
return userName;
```javascript
}
```javascript
public void setPasswd(String passwd) {
```javascript
this.passwd = passwd;
```javascript
}
```javascript
public String getPasswd() {
```javascript
return passwd;
```javascript
}
```javascript
public void setName(String name) {
```javascript
this.name = name;
```javascript
}
```javascript
public String getName() {
```javascript
return name;
```javascript
}
```javascript
public void setMobile(String mobile) {
```javascript
this.mobile = mobile;
```javascript
}
```javascript
public String getMobile() {
```javascript
return mobile;
```javascript
}
```javascript
public void setValid(String valid) {
```javascript
this.valid = valid;
```javascript
}
```javascript
public String getValid() {
```javascript
return valid;
```javascript
}
```javascript
public String getUserType() {
```javascript
return userType;
```javascript
}
```javascript
public void setUserType(String userType) {
```javascript
this.userType = userType;
```javascript
}
}
```javascript