讲解基于先后端分离和App模式的token受权开发,因此咱们代码主要写在spring-security-app里面。 以前的spring-security-demo项目一直是依赖咱们的spring-security-web项目,咱们如今改为spring-security-demo项目依赖咱们的:spring-security-app项目:html
<!-- <dependency> <artifactId>spring-security-web</artifactId> <groupId>com.yxm.security</groupId> <version>${project.version}</version> </dependency>--> <dependency> <groupId>com.yxm.security</groupId> <artifactId>spring-security-app</artifactId> <version>${project.version}</version> </dependency>
由于spring-security-app是一个空的项目,咱们如今启动下,看demo项目是否能够正常启动
启动项目时候报错:web
*************************** APPLICATION FAILED TO START *************************** Description: Field authenticationFailureHandler in com.yxm.security.core.validate.code.ValidateCodeFilter required a bean of type 'org.springframework.security.web.authentication.AuthenticationFailureHandler' that could not be found. Action: Consider defining a bean of type 'org.springframework.security.web.authentication.AuthenticationFailureHandler' in your configuration.
报错缘由是咱们的的spring-security-core须要一个类: spring
以前的认证成功和失败是在spring-security-web里面进行的,因为咱们web和app对认证失败后的处理是不同的,因此咱们在app里面咱们单独定义本身的认证失败,认证成功处理器。 数据库
而后咱们把SimpleResponse移动到spring-security-core里面的support包中。 后端
*************************** APPLICATION FAILED TO START *************************** Description: Field passwordEncoder in com.yxm.security.service.MyUserDetailsService required a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' that could not be found. Action: Consider defining a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' in your configuration.
咱们以前是在spring-security-web里面配置了浏览器
@Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
如今咱们在spring-security-web和spring-security-app都须要使用PasswordEncoder,因此咱们将其配置在spring-security-core里面的:SecurityCoreConfig服务器
@Configuration//配置类 @EnableConfigurationProperties(SecurityProperties.class)//让配置类生效 public class SecurityCoreConfig { @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
如今咱们服务正常启动。微信
@Configuration @EnableAuthorizationServer public class MyAuthorizationServerConfig { }
咱们知道以前受权码模式对于服务提供商而言:他要提供2个服务地址,一个是让用户跳过来去点击“受权”的服务地址;另一个就是:点完受权以后,带着受权码过来换access_token的地址。 app
咱们启动应用,发现console控制台打印出了不少地址: 前后端分离
/oauth/authorize(第一个地址):让用户跳过来去确认受权的路径。
/oauth/token(第二个地址):让用户去拿着受权码过来换access_token的地址路径。
咱们在浏览器访问下第一个地址:
咱们进入oauth2的官网:https://tools.ietf.org/html/rfc6749
去查找OAuth2的获取受权码的参数说明:
咱们如今主要是组装这几个参数:
response_type:值:code
client_id:值从咱们服务启动时候会打印出client_id:e0fb9540-74d1-4477-a8b7-b7bc89986176
redirect_uri:http://example.com
scope:all
state:可选
则url地址为:http://localhost:8088/oauth/authorize?response_type=code&client_id=e0fb9540-74d1-4477-a8b7-b7bc89986176&redirect_uri=http://example.com&scope=all
访问以后,弹出一个basic的信息,须要咱们输入用户名和密码:
咱们如今扮演的是服务提供商:qq,微信这种角色,那么这个地址帐号密码其实是咱们服务提供商提供给第三方应用的,让他去引导用户受权的,做为我来讲,我须要知道3件事:
总结就是:哪一个第三方应用须要哪一个用户进行什么权限。
用户名/密码是使用:UserDetailsService去校验
因此咱们输入的时候,用户名能够随便输入,面必须是123456便可。
而后咱们进入403页面:
由于在默认条件下:认证服务器请求必定要有一个ROLE_USER的角色才能访问
因此咱们在后面加一个ROLE_USER配置。
private SocialUserDetails buildUser(String userId){ // 根据用户名查找用户信息 //根据查找到的用户信息判断用户是否被冻结 String password = passwordEncoder.encode("123456"); logger.info("数据库密码是:"+password); return new SocialUser(userId, password, true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER")); }
在重启服务以前咱们作一个一个配置,以前咱们服务提供商提供的client_id是在应用启动时候,写死的。每次都要变,咱们本身来生成如下。
//咱们配置服务提供商给哪些第三方(client)应用去提供受权服务。咱们配置给yxm提供服务 security: oauth2: client: clientId: yxm secret: yxmsecret
重启服务此时咱们访问的url为:
http://localhost:8088/oauth/authorize?response_type=code&client_id=yxm&redirect_uri=http://example.com&scope=all
咱们就能够看到相似qq受权登陆,微信受权扫码的页面:
赞成或者拒绝受权是咱们服务提供方作的:咱们点击赞成:
而后带着咱们的受权码跳转到redirect_uri指定的页面-便是咱们第三方的页面。
第三方应用拿到受权码以后,应该拿着这个受权码去发一个请求换取:access_token;也就是调用咱们/oauth/token(第二个地址)
而后咱们使用浏览器请求工具发送获取access_token请求
咱们须要携带请求头Header:
Authorization:
request_body中内容以下:
grant_type:authorization_code
client_id:yxm
code:NBP3tW
redirect_uri:http://example.com
scope:all
而后咱们发送请求:这个时候返回以下:
密码模式至关因而服务提供方微信/qq把用户名/密码告诉了第三方,第三方拿着这个用户名/密码去获取access_token。服务提供方无法判断此用户名/密码来源。在微信这种服务提供方和第三方不是同一厂家的话,这种方式是不可行的,可是在咱们经常使用业务系统开发中用于咱们服务提供方和第三方App都是同一厂家是一伙的,因此可行。
与受权码的url和headers一致;只有BODY不同:
从地址:https://tools.ietf.org/html/r...
POST /token HTTP/1.1 Host:server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded
http://127.0.0.1:8088/oauth/token?grant_type=password&username=johndoe&password=123456&scope=all请求响应:
注意: 1.咱们发现使用受权码模式和使用密码模式获取的access_token是相同的。虽然受权模式不一样,可是使用的都是同一个用户:yxm;同一个用户他在去反复请求access_token时候,Spring Security OAuth会判断当前用户是否是发过响应请求access_token;只要这个token没有过时,只是这个expires_in在变小。
2.如今全部的4种受权都实现了,token的存储也实现了。实际上就用了一行代码:@EnableAuthorizationServer
自定义类,添加注解:@EnableResourceServer
@Configuration @EnableResourceServer public class MyResourceServerConfig { }
添加以上资源服务器配置以后,咱们访问下以前的用户接口
报401:
此时没有认证,咱们经过携带下access_token便可访问资源,可是重启服务后access_token已通过期了,咱们须要从新获取access_token而后再携带参数请求。
经过token_type与access_token合并。
如今流程走通了,可是仍是存在不少不足: