关于OAuth2不做介绍了,网络太多了。

按需求定制设计可以根据自己的需求进行定制,成都网站设计、成都网站制作、外贸网站建设构思过程中功能建设理应排到主要部位公司成都网站设计、成都网站制作、外贸网站建设的运用实际效果公司网站制作网站建立与制做的实际意义
环境:2.4.12 + OAuth2 + Redis
redis用来实现token的存储。
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.2.11.RELEASE
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
net.sourceforge.nekohtml
nekohtml
org.springframework.boot
spring-boot-starter-thymeleaf
server:
port: 8208
---
spring:
application:
name: oauth-server
---
spring:
redis:
host: localhost
port: 6379
password:
database: 1
lettuce:
pool:
maxActive: 8
maxIdle: 100
minIdle: 10
maxWait: -1
---
spring:
resources:
staticLocations: classpath:/static/,classpath:/templates/,classpath:/pages/
mvc:
staticPathPattern: /resources/**
---
spring:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
username: root
password: 123456
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimumIdle: 10
maximumPoolSize: 200
autoCommit: true
idleTimeout: 30000
poolName: MasterDatabookHikariCP
maxLifetime: 1800000
connectionTimeout: 30000
connectionTestQuery: SELECT 1
jpa:
hibernate:
ddlAuto: update
showSql: true
openInView: true #Open EntityManager in View
---
spring:
thymeleaf:
servlet:
contentType: text/html; charset=utf-8
cache: false
mode: LEGACYHTML5
encoding: UTF-8
enabled: true
prefix: classpath:/pages/
suffix: .html
---
spring:
main:
allow-bean-definition-overriding: true
@Entity
@Table(name = "T_APP")
public class App implements Serializable {
private static final long serialVersionUID = 1L ;
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id ;
/**
* 客户端ID
*/
private String clientId ;
/**
* 客户端密钥
*/
private String clientSecret ;
/**
* 跳转地址
*/
private String redirectUri ;
}
// 该实体用来存在每个应用的信息。
@Entity
@Table(name = "T_USERS")
public class Users implements UserDetails, Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id ;
private String username ;
private String password ;
}
// 该实体是用户登录信息。
// 提供了一个方法,根据clientId获取客户端信息。
public interface AppRepository extends JpaRepository, JpaSpecificationExecutor {
App findByClientId(String clientId) ;
}
public interface UsersRepository extends JpaRepository, JpaSpecificationExecutor {
Users findByUsernameAndPassword(String username, String password) ;
}
重要代码已经加了注释说明
@Configuration
@EnableAuthorizationServer
public class OAuthAuthorizationConfig extends AuthorizationServerConfigurerAdapter {
@Resource
private AppRepository appRepository ;
@Resource
private RedisConnectionFactory redisConnectionFactory ;
@Resource
private AuthenticationManager authenticationManager;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.tokenKeyAccess("permitAll()") // isAuthenticated()
.checkTokenAccess("permitAll()") // 允许访问 /oauth/check_token 接口
.allowFormAuthenticationForClients() ;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
// 自定义CODE
endpoints.authorizationCodeServices(new InMemoryAuthorizationCodeServices() {
@Override
public String createAuthorizationCode(OAuth2Authentication authentication) {
String code = UUID.randomUUID().toString().replaceAll("-", "") ;
store(code, authentication) ;
return code;
}
}) ;
endpoints.exceptionTranslator(new DefaultWebResponseExceptionTranslator() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public ResponseEntity translate(Exception e) throws Exception {
ResponseEntity responseEntity = super.translate(e) ;
ResponseEntity
密码模式必须设置对应的AuthenticationManager,所以这里必须暴露出来,否则系统找不到。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
该类主要是用在配置类中定义 ClientDetailsService是为了简化使用的。如下图:
图片
这里就是为了获取当前客户端的所有信息使用。
public class OAuthClientDetails implements ClientDetails,Serializable {
private static final long serialVersionUID = 1L;
private String id ;
private String clientId ;
private boolean secretRequired ;
private String clientSecret ;
private boolean scoped ;
private Set resourceIds ;
private Set scope = new HashSet<>();
private Set authorizedGrantTypes = new HashSet<>();
private Set registeredRedirectUri = new HashSet<>();
private Collection authorities ;
private boolean autoApprove ;
private Integer accessTokenValiditySeconds ;
private Integer refreshTokenValiditySeconds ;
}
@Component
public class LoginAuthenticationProvider implements AuthenticationProvider {
@Resource
private UsersRepository usersRepository ;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 登录用户名
String username = authentication.getName() ;
// 凭证(密码)
Object credentials = authentication.getCredentials() ;
Users user = null ;
try {
user = usersRepository.findByUsernameAndPassword(username, (String) credentials) ;
if (user == null) {
String errorMsg = "错误的用户名或密码" ;
throw new BadCredentialsException(errorMsg) ;
}
} catch (Exception e) {
throw e ;
}
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
user, authentication.getCredentials(), Arrays.asList(
new SimpleGrantedAuthority("ROLE_USERS"),
new SimpleGrantedAuthority("ROLE_ACTUATOR")));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
@Component
public class LoginPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString() ;
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return this.encode(rawPassword).equals(encodedPassword) ;
}
}
注意:
Users实体类为啥要实现UserDetails?
应该我们在存储token相关信息到redis时需要有对应key的生成方式。
RedisTokenStore.java中有个默认的key生成方式:
图片
图片
进入上面的方法中:
图片
进入getName方法中:
图片
最终它会调用红色框中的代码,这样就出现一个问题,你每次获取token的时候都会生成一个新的token。所以这里我们的Users实体实现了UserDetails接口。
图片
这里是通过debug查看
到此整合完毕了!
测试:
先造两条数据:
图片
图片
授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。
请求地址
图片
访问上面地址后跳转到了登录页面
输入正确的用户名密码后:
图片
成功后跳到了我们配置的跳转地址,这时候我们就可以根据地址栏的code获取token了:
图片
注意:这里的code是一次性的,也就是说如果使用过了就会自动失效。
密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。
在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。
图片
客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。
图片
简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
图片
简化模式的流程,这样有助于理解
(A)客户端将用户导向认证服务器。
(B)用户决定是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
(F)浏览器执行上一步获得的脚本,提取出令牌。
(G)浏览器将令牌发给客户端。
如果用户访问的时候,客户端的"访问令牌"过期前,可以申请一个新的访问令牌。
图片
这里的refresh_token就是在获取token的时候返回的。
Copyright © 2009-2022 www.wtcwzsj.com 青羊区广皓图文设计工作室(个体工商户) 版权所有 蜀ICP备19037934号