首先我们在了解oath2授权认证时,需要先了解Security认证,同时Security认证也是有许多种认证方式的我们这边主要学习数据库认证啦,首先我们需要三张表用户表,角色表,用户角色表。
同时需要在pom中导入Security:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
接下来我们就可以去实现Security认证啦,首先创建一个WebSecurityConfiguration类继承WebSecurityConfigurerAdapter,我就直接贴代码啦:
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private DomainUserDetailsService userDetailsService;
// 配置认证(用户名密码认证)
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new SCryptPasswordEncoder());
}
//配置授权(资源的访问规则)
@Override
protected void configure(HttpSecurity http) throws Exception {
//允许跨域访问
http.csrf().disable();
//允许直接访问
http.authorizeRequests().antMatchers("/login").permitAll();
//允许基于HttpServletRequest使用
http.authorizeRequests()
//对于/admin/** 只有ADMIN角色可以访问
.antMatchers("/admin/**")
.hasRole("ADMIN");
http.authorizeRequests()
//对于/test/** 只有ADMIN角色可以访问
.antMatchers("/test/**")
.hasRole("SUPER_ADMIN");
http.authorizeRequests()
//对于/user/** 只有USER角色可以访问
.antMatchers("/user/**")
.hasRole("USER");
http.authorizeRequests()
//对于/combination/** 同时具有两个角色才可以访问
.antMatchers("/combination/**")
.access("hasRole('ADMIN') and hasRole('USER')");
//对于没有匹配上的路径, 则需要认证, 不区分角色
// http.authorizeRequests().anyRequest().authenticated();
// 表单登录认证(通过浏览器访问的时候回跳转到/login登录页),如果不配置,则浏弹出览器自带的登录框
http.formLogin();
// httpbasic认证(通过postman访问时), 如果不配置则自动跳到登录页
http.httpBasic();
}
}
那么我们继续看看service是怎么实现的:
@Service
public class DomainUserDetailsService implements UserDetailsService {
@Autowired
JdbcTemplate jdbcTemplate;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Map<String, Object> userPo = jdbcTemplate.queryForMap("select * from auth_user where username='" + username + "'");
if (userPo == null) {
throw new UsernameNotFoundException("用户名不存在");
}
Integer id = (Integer) userPo.get("id");
String password = (String) userPo.get("password");
//用户权限
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
List<Map<String, Object>> list = jdbcTemplate.queryForList("SELECT r.`role_name` FROM auth_user_role u INNER JOIN auth_role r ON u.`role_id` = r.`id` WHERE u.user_id=" + id);
if (!CollectionUtils.isEmpty(list)) {
for (Map<String, Object> po : list) {
String roleCode = (String) po.get("role_name");
authorities.add(new SimpleGrantedAuthority(roleCode));
}
}
return new User(username, password, authorities);
}
}
这样整个Security认证算是完成了,接下来你们可以写几个controller进行测试啦!!!下面我们开始说oauth2授权啦:
oauth2授权应该是现在用的最为广泛的授权模式了吧,首先我们来了解一下他的授权原理吧
可能不是特别理解吧,接下来我们进入代码实现会比较清晰一点吧:
在pom配置oauth2所需jar包:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
配置yml文件:
#服务器配置
server:
#端口
port: 9060
#服务器发现注册配置
eureka:
client:
serviceUrl:
#配置服务中心(可配置多个,用逗号隔开)
defaultZone: http://localhost:9010/eureka
#spring配置
spring:
#应用配置
application:
#名称: OAuth2认证授权服务
name: service-oauth
#数据库配置
datasource:
druid:
# 数据库连接驱动
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123
filters: stat
max-active: 20
initial-size: 1
max-wait: 60000
min-idle: 1
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: select 1
test-while-idle: true
test-on-borrow: true
test-on-return: true
pool-prepared-statements: true
max-open-prepared-statements: 20
# redis配置
redis:
host: 127.0.0.1
port: 6379
password: 123456
jmx:
default-domain: service-oauth
# 打印sql
logging:
level:
com.ctkj.oauth.mapper : debug
认证服务器配:
package com.ctkj.oauth.config;
import com.ctkj.oauth.service.impl.DomainUserDetailsService;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
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.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import javax.sql.DataSource;
/**
* 认证服务器配置
*/
@Configuration
@EnableAuthorizationServer
public class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
DomainUserDetailsService domainUserDetailsService;
@Autowired
private DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetails());
//authorization_code模式测试地址:
// 第一步获取code(GET): http://localhost:9060/oauth/authorize?client_id=client&response_type=code&scope=select&redirect_uri=www.baidu.com
// 第二部获取token(POST): http://localhost:9060/oauth/token?grant_type=authorization_code&code=I2LK5r&client_id=client&client_secret=123456&redirect_uri=http://tets98y.com
//password模式测试地址(POST):http://localhost:9060/oauth/token?username=admin&password=123456&grant_type=password&scope=select&client_id=client&client_secret=123456
}
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//token放入redis中,进行存储
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager)
.userDetailsService(domainUserDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
//url:/oauth/token_key,exposes public key for token verification if using JWT tokens
.tokenKeyAccess("permitAll()")
//url:/oauth/check_token allow check token
.checkTokenAccess("isAuthenticated()")
//允许表单认证
.allowFormAuthenticationForClients();
}
}
资源服务:
package com.ctkj.oauth.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import javax.servlet.http.HttpServletRequest;
/**
* 配置资源服务器
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
//允许基于HttpServletRequest使用
// http.authorizeRequests()
// //对于/oauth/any/** 赋予任何人都可以访问的权限
// .antMatchers("/oauth/any/**").permitAll();
http.authorizeRequests()
//对于/oauth/admin/** 只有ADMIN角色可以访问
.antMatchers(HttpMethod.GET, "/admin/**")
.hasRole("ADMIN");
http.authorizeRequests()
//对于/oauth/user/** 只有USER角色可以访问
.antMatchers("/user/**")
.hasRole("USER");
http.authorizeRequests()
//对于/oauth/both/** 同时具有两个角色才可以访问
.antMatchers("test/**")
.access("hasRole('ADMIN') and hasRole('USER')");
}
}
授权服务器
package com.ctkj.oauth.config;
import com.ctkj.oauth.service.impl.DomainUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private DomainUserDetailsService userDetailsService;
// 配置认证(用户名密码认证)
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new SCryptPasswordEncoder());
}
//配置授权(资源的访问规则)
@Override
protected void configure(HttpSecurity http) throws Exception {
//允许跨域访问
http.csrf().disable();
http.cors().disable();
http.authorizeRequests().antMatchers("/oauth/**","/login").permitAll();
//对于没有匹配上的路径, 则需要认证, 不区分角色
http.authorizeRequests().anyRequest().authenticated();
// httpbasic认证(通过postman访问时),如果不配置则自动跳到登录页
http.httpBasic();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
同时还需要在加一张表:客户端授权表:
这样算配好啦,oauth2授权模式有4种,这边我只做两种授权模式的一个测试啦,接下来咱们一起走一遍测试吧:
授权码模式:
第一步获取code:
http://localhost:9060/oauth/authorize?client_id=web&response_type=code&scope=web&redirect_uri=www.iuyr.com该链接的参数对于数据库字段中的value值就好了。response_type授权类型我们设置为code就可以了。
第二步我们会进入一个登陆页面
登陆成功后会返回一串地址该地址是你设置的地址后面的code就是用来获取token的:
第三步通过code获取token,我们采用postman去获取:
这样整体授权码模式就完成了!!!快去试试用token访问不同的地址看看效果吧!
第二种授权模式:password授权这个也是我们最常用的授权模式:
第一步直接访问:
这样你就可以拿到token了,然后可以开始测试了!!!!
这样最简单的一个授权认证模块就算完成啦