首先我们在了解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了,然后可以开始测试了!!!! 这样最简单的一个授权认证模块就算完成啦
