springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)

本文主要是介绍springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)

项目security_simple(认证授权项目)

1.新建springboot项目

 

 

这儿选择springboot版本我选择的是2.0.6

点击finish后完成项目的创建

2.引入maven依赖  下面是我引入的依赖

复制代码

  1 <?xml version="1.0" encoding="UTF-8"?>2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">4     <modelVersion>4.0.0</modelVersion>5 6     <groupId>com.megalith</groupId>7     <artifactId>security_simple</artifactId>8     <version>0.0.1-SNAPSHOT</version>9     <packaging>jar</packaging>10 11     <name>security_simple</name>12     <description>Demo project for Spring Boot</description>13 14     <parent>15         <groupId>org.springframework.boot</groupId>16         <artifactId>spring-boot-starter-parent</artifactId>17         <version>2.0.6.RELEASE</version>18         <relativePath/> <!-- lookup parent from repository -->19     </parent>20 21     <properties>22         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>23         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>24         <java.version>1.8</java.version>25         <pagehelper.version>1.1.0</pagehelper.version>26         <mybatis.generator.version>1.3.2</mybatis.generator.version>27         <fastjson.version>1.2.31</fastjson.version>28         <jackson.version>2.9.7</jackson.version>29     </properties>30 31     <dependencies>32         <dependency>33             <groupId>junit</groupId>34             <artifactId>junit</artifactId>35             <version>4.11</version>36             <scope>test</scope>37         </dependency>38 39         <dependency>40             <groupId>org.springframework.boot</groupId>41             <artifactId>spring-boot-starter-web</artifactId>42             <exclusions>43                 <exclusion>44                     <groupId>org.springframework.boot</groupId>45                     <artifactId>spring-boot-starter-tomcat</artifactId>46                 </exclusion>47             </exclusions>48         </dependency>49         <dependency>50             <groupId>org.springframework.boot</groupId>51             <artifactId>spring-boot-configuration-processor</artifactId>52             <optional>true</optional>53         </dependency>54         <dependency>55             <groupId>org.springframework.boot</groupId>56             <artifactId>spring-boot-starter-jetty</artifactId>57         </dependency>58 59 60         <!-- aop -->61         <dependency>62             <groupId>org.springframework.boot</groupId>63             <artifactId>spring-boot-starter-aop</artifactId>64         </dependency>65         <!-- json处理 -->66         <dependency>67             <groupId>com.alibaba</groupId>68             <artifactId>fastjson</artifactId>69             <version>${fastjson.version}</version>70         </dependency>71 72         <!-- jackson json begin -->73         <dependency>74             <groupId>com.fasterxml.jackson.core</groupId>75             <artifactId>jackson-core</artifactId>76             <version>${jackson.version}</version>77         </dependency>78         <dependency>79             <groupId>com.fasterxml.jackson.core</groupId>80             <artifactId>jackson-databind</artifactId>81             <version>${jackson.version}</version>82         </dependency>83         <dependency>84             <groupId>com.fasterxml.jackson.core</groupId>85             <artifactId>jackson-annotations</artifactId>86             <version>${jackson.version}</version>87         </dependency>88         <dependency>89             <groupId>com.fasterxml.jackson.module</groupId>90             <artifactId>jackson-module-jaxb-annotations</artifactId>91             <version>${jackson.version}</version>92         </dependency>93         <!-- jackson json end -->94         <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->95         <dependency>96             <groupId>org.apache.commons</groupId>97             <artifactId>commons-lang3</artifactId>98             <version>3.3.2</version>99         </dependency>
100         <dependency>
101             <groupId>com.fasterxml.uuid</groupId>
102             <artifactId>java-uuid-generator</artifactId>
103             <version>3.1.3</version>
104         </dependency>
105 
106 
107         <!-- druid datasource -->
108         <dependency>
109             <groupId>com.alibaba</groupId>
110             <artifactId>druid</artifactId>
111             <version>1.0.27</version>
112         </dependency>
113         <!-- mybatis -->
114         <dependency>
115             <groupId>org.mybatis.spring.boot</groupId>
116             <artifactId>mybatis-spring-boot-starter</artifactId>
117             <version>1.3.0</version>
118         </dependency>
119         <!--mybatis代码生成器 -->
120         <dependency>
121             <groupId>org.mybatis.generator</groupId>
122             <artifactId>mybatis-generator-core</artifactId>
123             <version>${mybatis.generator.version}</version>
124         </dependency>
125         <!-- bean验证 -->
126         <dependency>
127             <groupId>org.springframework.boot</groupId>
128             <artifactId>spring-boot-starter-validation</artifactId>
129         </dependency>
130 
131         <!-- mysql驱动 -->
132         <dependency>
133             <groupId>mysql</groupId>
134             <artifactId>mysql-connector-java</artifactId>
135             <scope>runtime</scope>
136         </dependency>
137         <!-- spring boot 开发工具 -->
138         <dependency>
139             <groupId>org.springframework.boot</groupId>
140             <artifactId>spring-boot-devtools</artifactId>
141             <optional>true</optional>
142         </dependency>
143         <!-- 测试 -->
144         <dependency>
145             <groupId>org.springframework.boot</groupId>
146             <artifactId>spring-boot-starter-test</artifactId>
147             <scope>test</scope>
148         </dependency>
149 
150 
151         <dependency>
152             <groupId>org.springframework.boot</groupId>
153             <artifactId>spring-boot-starter-data-jpa</artifactId>
154         </dependency>
155 
156         <!--spring-security-oauth2 begin-->
157         <dependency>
158             <groupId>org.springframework.boot</groupId>
159             <artifactId>spring-boot-starter-security</artifactId>
160         </dependency>
161         <dependency>
162             <groupId>org.springframework.security.oauth.boot</groupId>
163             <artifactId>spring-security-oauth2-autoconfigure</artifactId>
164             <version>2.0.6.RELEASE</version>
165         </dependency>
166         <!--spring-security-oauth2 end-->
167     </dependencies>
168 
169     <build>
170         <plugins>
171             <plugin>
172                 <groupId>org.springframework.boot</groupId>
173                 <artifactId>spring-boot-maven-plugin</artifactId>
174             </plugin>
175         </plugins>
176     </build>
177 
178 
179 </project>

复制代码

3.新建数据库(因为本项目采用jdbc的形式存储token相关)

  建表sql语句的地址为    https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql  这儿记得不同的数据库中的特殊字段需要修改一下,建好的表如下

 

4.进行配置

  4.1 CustomAuthorizationServerConfig 类

  

复制代码

  1 import org.springframework.beans.factory.annotation.Autowired;2 import org.springframework.context.annotation.Bean;3 import org.springframework.context.annotation.Configuration;4 import org.springframework.http.HttpHeaders;5 import org.springframework.security.authentication.AuthenticationManager;6 import org.springframework.security.core.userdetails.UserDetailsService;7 import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;8 import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;9 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;10 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;11 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;12 import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;13 import org.springframework.security.oauth2.provider.token.TokenStore;14 import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;15 import org.springframework.web.cors.CorsConfiguration;16 import org.springframework.web.cors.CorsConfigurationSource;17 import org.springframework.web.filter.CorsFilter;18 19 import javax.annotation.Resource;20 import javax.servlet.http.HttpServletRequest;21 import javax.sql.DataSource;22 23 /**24  * @Description: 配置授权认证服务类25  * @author: zhoum26  * @Date: 2018-11-2227  * @Time: 13:4128  */29 @Configuration30 @EnableAuthorizationServer //授权认证中心31 public class CustomAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {32 33     /**34      * 权限验证控制器35      */36     @Autowired37     private AuthenticationManager authenticationManager;38     /**39      * 数据源,保存token的时候需要 默认为spring中配置的datasource40      */41     @Autowired42     private DataSource dataSource;43     /**44      * 设置保存token的方式,一共有五种,这里采用数据库的方式45      */46     @Autowired47     private TokenStore tokenStore;48 49     @Bean50     public TokenStore tokenStore() {51         return new JdbcTokenStore(dataSource);52     }53 54     /**55      * 自定义登录或者鉴权失败时的返回信息56      */57     @Resource(name = "webResponseExceptionTranslator")58     private WebResponseExceptionTranslator webResponseExceptionTranslator;59     60     61     /******************配置区域**********************/62 63 64     /**65      * 用来配置授权(authorizatio)以及令牌(token)的访问端点和令牌服务   核心配置  在启动时就会进行配置66      */67     @Override68     public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {69         //开启密码授权类型70         endpoints.authenticationManager(authenticationManager);71         //配置token存储方式72         endpoints.tokenStore(tokenStore);73         //自定义登录或者鉴权失败时的返回信息74         endpoints.exceptionTranslator(webResponseExceptionTranslator);75     }76 77     /**78      * 用来配置令牌端点(Token Endpoint)的安全约束.79      */80     @Override81     public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {82         /**83          * 配置oauth2服务跨域84          */85         CorsConfigurationSource source = new CorsConfigurationSource() {86             @Override87             public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {88                 CorsConfiguration corsConfiguration = new CorsConfiguration();89                 corsConfiguration.addAllowedHeader("*");90                 corsConfiguration.addAllowedOrigin(request.getHeader(HttpHeaders.ORIGIN));91                 corsConfiguration.addAllowedMethod("*");92                 corsConfiguration.setAllowCredentials(true);93                 corsConfiguration.setMaxAge(3600L);94                 return corsConfiguration;95             }96         };97 98         security.tokenKeyAccess("permitAll()")99                 .checkTokenAccess("permitAll()")
100                 .allowFormAuthenticationForClients()
101                 .addTokenEndpointAuthenticationFilter(new CorsFilter(source));
102     }
103 
104     /**
105      * 用来配置客户端详情服务(ClientDetailsService),
106      * 客户端详情信息在这里进行初始化,  数据库在进行client_id 与 client_secret验证时   会使用这个service进行验证
107      */
108     @Override
109     public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
110         clients.jdbc(dataSource);
111     }
112 }

复制代码

  注意: 1. authenticationManager 的配置类会在后面的WebSecurityAdaptConfig配置中给出

      2.TokenStore 一共有五种配置方式  本项目采用jdbc也就是数据库的形式

      

      3.WebResponseExceptionTranslator为自定义的验证错误异常返回类,代码如下,其中responsedata为自定义的数据返回类 包含code,message,data三个属性

        

复制代码

 1 import cn.com.megalith.common.ResponseData;2 import org.springframework.context.annotation.Bean;3 import org.springframework.context.annotation.Configuration;4 import org.springframework.http.HttpHeaders;5 import org.springframework.http.HttpStatus;6 import org.springframework.http.ResponseEntity;7 import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;8 import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;9 import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
10 
11 /**
12  * @Description: web全局异常返回处理器
13  * @author: zhoum
14  * @Date: 2018-11-22
15  * @Time: 14:48
16  */
17 @Configuration
18 public class WebResponseExceptionTranslateConfig {
19     /**
20      * 自定义登录或者鉴权失败时的返回信息
21      */
22     @Bean(name = "webResponseExceptionTranslator")
23     public WebResponseExceptionTranslator webResponseExceptionTranslator() {
24         return new DefaultWebResponseExceptionTranslator() {
25             @Override
26             public ResponseEntity translate(Exception e) throws Exception {
27                 ResponseEntity responseEntity = super.translate(e);
28                 OAuth2Exception body = (OAuth2Exception) responseEntity.getBody();
29                 HttpHeaders headers = new HttpHeaders();
30                 headers.setAll(responseEntity.getHeaders().toSingleValueMap());
31                 // do something with header or response
32                 if ( 400 == responseEntity.getStatusCode().value() ) {
33                     System.out.println(body.getMessage());
34                     if ( "Bad credentials".equals(body.getMessage()) ) {
35                         return new ResponseEntity(new ResponseData("400" , "您输入的用户名或密码错误" , null) , headers , HttpStatus.OK);
36                     }
37                 }
38                 return new ResponseEntity(body , headers , responseEntity.getStatusCode());
39 
40             }
41         };
42     }
43 }

复制代码

 

  4.2ResourceServerConfig

  

复制代码

 1 import org.springframework.context.annotation.Configuration;2 import org.springframework.security.config.annotation.web.builders.HttpSecurity;3 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;4 import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;5 6 /**7  * @Description: 资源提供端的配置  8  * @author: zhoum9  * @Date: 2018-11-22
10  * @Time: 16:58
11  */
12 @Configuration
13 @EnableResourceServer //开启资源提供服务的配置  是默认情况下spring security oauth2的http配置   会被WebSecurityConfigurerAdapter的配置覆盖
14 public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
15 
16     @Override
17     public void configure(HttpSecurity http) throws Exception {
18         http
19                 .authorizeRequests()
20                 .antMatchers("/**").permitAll();
21     }
22 }

复制代码

  这儿注意,后面的WebSecurityAdaptConfig的配置类会覆盖这儿的配置

  4.3 WebSecurityAdaptConfig  配置类

  

复制代码

 1 import cn.com.megalith.auth.CustomerAuthenticationProvider;2 import org.springframework.beans.factory.annotation.Autowired;3 import org.springframework.context.annotation.Bean;4 import org.springframework.context.annotation.Configuration;5 import org.springframework.security.authentication.AuthenticationManager;6 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;7 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;8 import org.springframework.security.config.annotation.web.builders.HttpSecurity;9 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
10 import org.springframework.security.core.userdetails.UserDetailsService;
11 import org.springframework.security.crypto.password.PasswordEncoder;
12 
13 import javax.annotation.Resource;
14 
15 /**
16  * @Description: //是默认情况下spring security的http配置 优于ResourceServerConfigurerAdapter的配置
17  * @author: zhoum
18  * @Date: 2018-11-22
19  * @Time: 17:06
20  */
21 @Configuration              //开启三种可以在方法上面加权限控制的注解
22 @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
23 public class WebSecurityAdaptConfig extends WebSecurityConfigurerAdapter {
24 
25     /**
26      * 获取用户的验证配置类
27      */
28     @Resource(name = "signUserDetailService")
29     private UserDetailsService userDetailsService;
30     /**
31      * 加密配置
32      */
33     @Autowired
34     private PasswordEncoder passwordEncoder;
35 
36     /**
37      * 密码验证处理器
38      */
39     @Resource(name = "myCustomerAuthenticationProvider")
40     private CustomerAuthenticationProvider customerAuthenticationProvider;
41 
42     /**
43      * spring security设置
44      */
45     @Override
46     protected void configure(HttpSecurity http) throws Exception {
47         http
48                 .authorizeRequests()//定义哪些url需要被保护  哪些不需要保护
49                 .antMatchers("/oauth/token" , "oauth/check_token").permitAll()//定义这两个链接不需要登录可访问
50                 .antMatchers("/**").permitAll() //定义所有的都不需要登录  目前是测试需要
51                 .anyRequest().authenticated() //其他的都需要登录
52                 //.antMatchers("/sys/**").hasRole("admin")///sys/**下的请求 需要有admin的角色
53                 .and()
54                 .formLogin().loginPage("/login")//如果未登录则跳转登录的页面   这儿可以控制登录成功和登录失败跳转的页面
55                 .usernameParameter("username").passwordParameter("password").permitAll()//定义号码与密码的parameter
56                 .and()
57                 .csrf().disable();//防止跨站请求  spring security中默认开启
58 
59 
60     }
61 
62     /**
63      * 权限管理器  AuthorizationServerConfigurerAdapter认证中心需要的AuthenticationManager需要
64      */
65     @Override
66     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
67         //目的是为了前端获取数据时获取到整个form-data的数据,提供验证器
68         auth.authenticationProvider(customerAuthenticationProvider);
69         //配置登录user验证处理器  以及密码加密器  好让认证中心进行验证
70         auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
71     }
72 
73     /**
74      * 需要配置这个支持password模式
75      * support password grant type
76      *
77      * @return
78      * @throws Exception
79      */
80     @Override
81     @Bean
82     public AuthenticationManager authenticationManagerBean() throws Exception {
83         return authenticationManager();
84     }
85 }

复制代码

  注意: 1. UserDetailsService接口中主要包含有一个UserDetails loadUserByUsername(String var1) 方法,其中var1代表username即登录时输入的用户名,本项目的配置如下

      

复制代码

 1 import cn.com.megalith.auth.UserDetail;2 import cn.com.megalith.domain.entity.User;3 import cn.com.megalith.service.IUserService;4 import org.springframework.beans.factory.annotation.Autowired;5 import org.springframework.security.core.userdetails.UserDetails;6 import org.springframework.security.core.userdetails.UserDetailsService;7 import org.springframework.security.core.userdetails.UsernameNotFoundException;8 import org.springframework.stereotype.Component;9 
10 /**
11  * @Description:
12  * @author: zhoum
13  * @Date: 2018-11-22
14  * @Time: 15:07
15  */
16 @Component("signUserDetailService")
17 public class SignUserDetaiServiceConfig implements UserDetailsService {
18 
19     @Autowired
20     private IUserService userService;
21 
22     /**
23      * 启动刷新token授权类型,会判断用户是否还是存活的
24      */
25     @Override
26     public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
27         User currentUser = userService.getByName(s);
28         if ( currentUser == null ) {
29             throw new UsernameNotFoundException("用户没用找到");
30         }
31 
32         return new UserDetail(currentUser);
33     }
34 }

复制代码

  这儿需要返回一个UserDetails的实现类用于spring security中验证 本项目的配置如下  其中的user为自定义的user实体

复制代码

  1 import cn.com.megalith.domain.entity.User;2 import org.springframework.context.ApplicationContext;3 import org.springframework.security.core.GrantedAuthority;4 import org.springframework.security.core.authority.SimpleGrantedAuthority;5 import org.springframework.security.core.userdetails.UserDetails;6 7 import java.util.ArrayList;8 import java.util.Collection;9 import java.util.List;10 11 /**12  * @Description: 用户信息的实体类13  * @author: zhoum14  * @Date: 2018-11-2215  * @Time: 15:4616  */17 public class UserDetail implements UserDetails {18 19     private User user;20     21     private String id;22 23     /**24      * 通过构造方法在UserDetailsService的方法中将查到的user注入进去25      */26     public UserDetail(User user) {27         this.user = user;28         if ( user != null ) {29             this.id = user.getId();30         }31     }32     /**33      * 对当前的用户赋予其应有的权限34      */35     @Override36     public Collection<? extends GrantedAuthority> getAuthorities() {37         //添加权限 这儿暂时写死  应该从数据库中拿到38         List authorisList = new ArrayList();39         authorisList.add(new SimpleGrantedAuthority("ROLE_AA"));40         authorisList.add(new SimpleGrantedAuthority("ROLE_BB"));41         authorisList.add(new SimpleGrantedAuthority("ROLE_CC"));42         authorisList.add(new SimpleGrantedAuthority("ROLE_DD"));43         return authorisList;44     }45     /**46      * 获取密码47      */48     @Override49     public String getPassword() {50         return user.getPassword();51     }52     /**53      * 获取用户名54      */55     @Override56     public String getUsername() {57         return user.getUsername();58     }59     /**60      * 账户是否未过期61      */62     @Override63     public boolean isAccountNonExpired() {64         return true;65     }66     /**67      * 账户是否未锁定68      */69     @Override70     public boolean isAccountNonLocked() {71         return true;72     }73     /**74      * 证书是否未过期75      */76     @Override77     public boolean isCredentialsNonExpired() {78         return true;79     }80 81     /**82      * 是否有效   可对应数据库中的delete_flag字段83      */84     @Override85     public boolean isEnabled() {86         return true;87     }88 89     public User getUser() {90         return user;91     }92 93     public void setUser(User user) {94         this.user = user;95     }96 97     public String getId() {98         return id;99     }
100 
101     public void setId(String id) {
102         this.id = id;
103     }
104 }

复制代码

    注意: 2. PasswordEncoder 主要为加密管理器  用于密码的加密匹配  本项目的配置如下

复制代码

 1 import org.springframework.context.annotation.Configuration;2 import org.springframework.security.crypto.password.PasswordEncoder;3 import org.springframework.stereotype.Component;4 5 import java.security.MessageDigest;6 7 /**8  * @Description:MD5加密9  * @author: zhoum
10  * @Date: 2018-11-22
11  * @Time: 17:36
12  */
13 @Component
14 public class EncodePassword implements PasswordEncoder {
15 
16     /**
17      * md5加密
18      */
19     @Override
20     public String encode(CharSequence charSequence) {
21 
22         return MD5(charSequence.toString());
23     }
24 
25     /**
26      * 匹配规则
27      */
28     @Override
29     public boolean matches(CharSequence charSequence , String s) {
30         return MD5(charSequence.toString()).equals(s);
31     }
32 
33     /**
34      * md5加密过程
35      */
36     public static String MD5(String key) {
37         char hexDigits[] = {
38                 '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f'
39         };
40         try {
41             byte[] btInput = key.getBytes();
42             // 获得MD5摘要算法的 MessageDigest 对象
43             MessageDigest mdInst = MessageDigest.getInstance("MD5");
44             // 使用指定的字节更新摘要
45             mdInst.update(btInput);
46             // 获得密文
47             byte[] md = mdInst.digest();
48             // 把密文转换成十六进制的字符串形式
49             int j = md.length;
50             char str[] = new char[ j * 2 ];
51             int k = 0;
52             for (int i = 0; i < j; i++) {
53                 byte byte0 = md[ i ];
54                 str[ k++ ] = hexDigits[ byte0 >>> 4 & 0xf ];
55                 str[ k++ ] = hexDigits[ byte0 & 0xf ];
56             }
57             return new String(str);
58         } catch (Exception e) {
59             return null;
60         }
61     }
62 }

复制代码

 

    注意: 3.CustomerAuthenticationProvider 主要是为AuthenticationProvider借接口的实现类  为spring security提供密码验证器  是核心配置,这儿需要注意  系统中已经有相应的实现类,如果不配置,则系统中会默认使用org.springframework.security.authentication.dao.DaoAuthenticationProvider这个类来进行验证,DaoAuthenticationProvider这个类继承了org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider这个抽象类,所以我们要自定义provider验证流程可以实现AuthenticationProvider接口或者继承AbstractUserDetailsAuthenticationProvider抽象类均可,下面是两种模式的代码

    (1)实现AuthenticationProvider形式  自己写验证流程

      

复制代码

 1 import org.slf4j.Logger;2 import org.slf4j.LoggerFactory;3 import org.springframework.beans.factory.annotation.Autowired;4 import org.springframework.security.authentication.AuthenticationProvider;5 import org.springframework.security.authentication.BadCredentialsException;6 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;7 import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;8 import org.springframework.security.core.Authentication;9 import org.springframework.security.core.AuthenticationException;
10 import org.springframework.security.core.userdetails.UserDetails;
11 import org.springframework.security.core.userdetails.UserDetailsService;
12 import org.springframework.security.crypto.password.PasswordEncoder;
13 import org.springframework.stereotype.Component;
14 
15 import javax.annotation.Resource;
16 import java.util.Objects;
17 
18 /**
19  * @Description: AuthenticationManagerBuilder中的AuthenticationProvider是进行认证的核心
20  * @author: zhoum
21  * @Date: 2018-11-23
22  * @Time: 9:11
23  */
24 @Component("myCustomerAuthenticationProvider")
25 public class CustomerAuthenticationProvider implements AuthenticationProvider {
26 
27     @Resource(name = "signUserDetailService")
28     private UserDetailsService userDetailsService;
29 
30    public static final Logger LOGGER = LoggerFactory.getLogger(CustomerAuthenticationProvider.class);
31 
32     /**
33      *authentication是前台拿过来的号码密码bean  主要验证流程代码  注意这儿懒得用加密验证!!!
34      */
35     @Override
36     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
37         LOGGER.info("用户输入的用户名是:" + authentication.getName());
38         LOGGER.info("用户输入的密码是:" + authentication.getCredentials());
39         // 根据用户输入的用户名获取该用户名已经在服务器上存在的用户详情,如果没有则返回null
40         UserDetails userDetails = userDetailsService.loadUserByUsername(authentication.getName());
41         try {
42             LOGGER.info("服务器上已经保存的用户名是:" + userDetails.getUsername());
43             LOGGER.info("服务器上保存的该用户名对应的密码是: " + userDetails.getPassword());
44             LOGGER.info("服务器上保存的该用户对应的权限是:" + userDetails.getAuthorities());
45             if(authentication.getCredentials().equals(userDetails.getPassword())){
46                 //验证成功  将返回一个UsernamePasswordAuthenticaionToken对象
47                 LOGGER.info("LOGIN SUCCESS !!!!!!!!!!!!!!!!!!!");
48                 //分别返回用户实体   输入的密码   以及用户的权限
49                 return new UsernamePasswordAuthenticationToken(userDetails,authentication.getCredentials(),userDetails.getAuthorities());
50             }
51         } catch (Exception e){
52             LOGGER.error("author failed, -------------------the error message is:-------- " + e);
53             throw e;
54         }
55         //如果验证不同过则返回null或者抛出异常
56         return null;
57     }
58 
59     /**
60      *
61      **/
62     @Override
63     public boolean supports(Class<?> aClass) {
64         return true;
65     }
66 }

复制代码

    这儿会返回一个UsernamePasswordAuthenticationToken对象,

    

    (2)继承AbstractUserDetailsAuthenticationProvider抽象类  这儿主要是复写取user和验证流程  这儿直接用了DaoAuthenticationProvider中的验证代码  有兴趣的可以自己重新写验证代码

复制代码

 1 import org.slf4j.Logger;2 import org.slf4j.LoggerFactory;3 import org.springframework.beans.factory.annotation.Autowired;4 import org.springframework.security.authentication.AuthenticationProvider;5 import org.springframework.security.authentication.BadCredentialsException;6 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;7 import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;8 import org.springframework.security.core.Authentication;9 import org.springframework.security.core.AuthenticationException;
10 import org.springframework.security.core.userdetails.UserDetails;
11 import org.springframework.security.core.userdetails.UserDetailsService;
12 import org.springframework.security.crypto.password.PasswordEncoder;
13 import org.springframework.stereotype.Component;
14 
15 import javax.annotation.Resource;
16 import java.util.Objects;
17 
18 /**
19  * @Description: AuthenticationManagerBuilder中的AuthenticationProvider是进行认证的核心
20  * @author: zhoum
21  * @Date: 2018-11-23
22  * @Time: 9:11
23  */
24 @Component("myCustomerAuthenticationProvider")
25 public class CustomerAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
26 
27     @Resource(name = "signUserDetailService")
28     private UserDetailsService userDetailsService;
29 
30     @Autowired
31     private PasswordEncoder passwordEncoder;
32 
33     /**
34      * 手动实现认证
35      */
36     @Override
37     protected void additionalAuthenticationChecks(UserDetails userDetails , UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
38         if ( authentication.getCredentials() == null ) {
39             this.logger.debug("Authentication failed: no credentials provided");
40             throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials" , "Bad credentials"));
41         } else {
42             String presentedPassword = authentication.getCredentials().toString();
43             if ( !this.passwordEncoder.matches(presentedPassword , userDetails.getPassword()) ) {
44                 this.logger.debug("Authentication failed: password does not match stored value");
45                 throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials" , "Bad credentials"));
46             }
47         }
48     }
49 
50     /**
51      * 手动加载user
52      */
53     @Override
54     protected UserDetails retrieveUser(String s , UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
55         return userDetailsService.loadUserByUsername(s);
56     }
57 }

复制代码

    注意4:configure(HttpSecurity http)会覆盖ResourceServerConfig中的配置

  4.4 配置ajax跨域

  

复制代码

 1 import org.springframework.context.annotation.Bean;2 import org.springframework.context.annotation.Configuration;3 import org.springframework.web.cors.CorsConfiguration;4 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;5 import org.springframework.web.filter.CorsFilter;6 7 /**8  * @Description:9  * @author: zhoum
10  * @Date: 2018-11-22
11  * @Time: 17:09
12  */
13 @Configuration
14 public class WebOrignAllowConfig {
15     /**
16      * @return org.springframework.web.filter.CorsFilter
17      * @Author zhoum
18      * @Description 解决跨域问题
19      * @Date 17:10 2018-11-22
20      * @Param []
21      **/
22     @Bean
23     public CorsFilter corsFilter() {
24         final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
25         final CorsConfiguration corsConfiguration = new CorsConfiguration();
26         corsConfiguration.setAllowCredentials(true);
27         corsConfiguration.addAllowedOrigin("*");
28         corsConfiguration.addAllowedHeader("*");
29         corsConfiguration.addAllowedMethod("*");
30         urlBasedCorsConfigurationSource.registerCorsConfiguration("/**" , corsConfiguration);
31         return new CorsFilter(urlBasedCorsConfigurationSource);
32     }
33 }

复制代码

 

5. 其他项目类即根据User的bean创建出UserController   UserService  UserMapper等  代码可以查看git上的源码

6. 项目的属性配置 application.yml

  

复制代码

spring:#色彩日志输出output:ansi:enabled: alwaysdatasource:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/tjfx?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&allowPublicKeyRetrieval=truetype: com.alibaba.druid.pool.DruidDataSource#连接池配置driverClassName: com.mysql.jdbc.Driver# 初始化大小,最小,最大initialSize: 5minIdle: 5maxActive: 20# 配置获取连接等待超时的时间maxWait: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: false# 打开PSCache,并且指定每个连接上PSCache的大小poolPreparedStatements: truemaxPoolPreparedStatementPerConnectionSize: 20# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙filters: stat,log4j# 通过connectProperties属性来打开mergeSql功能;慢SQL记录connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000mybatis:mapper-locations: classpath*:**/mappers/*.xmltype-aliases-package: cn.com.megalith.domain.entityconfiguration:map-underscore-to-camel-case: trueuse-generated-keys: trueuse-column-label: truecache-enabled: truecall-setters-on-nulls: true
logging:level:root: infoorg:springframework: info
server:port: 8081

复制代码

7.启动类SecurityApplication

  

复制代码

 1 import org.mybatis.spring.annotation.MapperScan;2 import org.springframework.boot.SpringApplication;3 import org.springframework.boot.autoconfigure.SpringBootApplication;4 import org.springframework.transaction.annotation.EnableTransactionManagement;5 6 @SpringBootApplication7 @MapperScan("cn.com.megalith.dao")8 @EnableTransactionManagement9 public class SecurityApplication {
10 
11     public static void main(String[] args) {
12         SpringApplication.run(SecurityApplication.class , args);
13     }

复制代码

8.项目整体效果如下(不要在意红色,编辑器故障了)

 

 9.进行测试  

  (1) 获取token

  

  注意:其中的client_id  与 client_secret即在oauth_client_details中的数据  这儿是我自己手动插入的一条  一般在申请别的网站的授权时应该会得到这个  client_secret是md5加密后的结果

  如果号码密码正确则会返回  红色框内即为得到的token

  

 

  如果密码错误 则会返回刚自定义的错误返回结果

  

  (2)进行方法验证

复制代码

 1 @RestController2 @RequestMapping("/user")3 public class UserController {4 5     @GetMapping("/current")6     @Secured("ROLE_AA")7     public String getCurrentUser(Principal principal) {8         System.out.println(principal);9         return "111";
10     }
11 }

复制代码

  访问方式:记得加上token  不然会验证不通过

  

 

 

 

 如果有权限 则会返回

  且principal即为获取到的当前用户的对象  具体结构如下

 

   验证失败则会返回

  如果无权限则会返回

  

以上为授权认证端与资源服务端在同一项目时,如果为不同的项目则资源服务的项目可以单独如下创建项目

 

 

项目customer_simple(资源服务项目)

1.创建项目

   与创建上面授权项目完全类似,依赖一样,所以略过

2.配置类

  

  (1)ResourceServiceConfig

  

复制代码

 1 import org.springframework.context.annotation.Configuration;2 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;3 import org.springframework.security.config.annotation.web.builders.HttpSecurity;4 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;5 import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;6 import org.springframework.web.cors.CorsConfiguration;7 8 import javax.servlet.http.HttpServletResponse;9 
10 /**
11  * @Description: 配置资源服务器
12  * @author: zhoum
13  * @Date: 2018-11-26
14  * @Time: 15:08
15  */
16 @Configuration
17 @EnableResourceServer
18 @EnableGlobalMethodSecurity(securedEnabled = true)
19 public class ResourceServiceConfig extends ResourceServerConfigurerAdapter {
20 
21     @Override
22     public void configure(HttpSecurity http) throws Exception {
23         http
24                 .csrf().disable()
25                 .exceptionHandling()
26                 .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
27                 .and()
28                 .authorizeRequests()
29                 .antMatchers("/**").permitAll()
30                 //跨域配置
31                 .and().cors().configurationSource(request -> {
32             CorsConfiguration corsConfiguration = new CorsConfiguration();
33             corsConfiguration.addAllowedHeader("*");
34             corsConfiguration.addAllowedOrigin(request.getHeader("Origin"));
35             corsConfiguration.addAllowedMethod("*");
36             corsConfiguration.setAllowCredentials(true);
37             corsConfiguration.setMaxAge(3600L);
38             return corsConfiguration;
39         });
40     }
41 }

复制代码

  (2) 跨域配置类

  

复制代码

 1 import org.springframework.context.annotation.Bean;2 import org.springframework.context.annotation.Configuration;3 import org.springframework.web.cors.CorsConfiguration;4 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;5 import org.springframework.web.filter.CorsFilter;6 7 /**8  * @Description:9  * @author: zhoum
10  * @Date: 2018-11-22
11  * @Time: 17:09
12  */
13 @Configuration
14 public class WebOrignAllowConfig {
15     /**
16      * @return org.springframework.web.filter.CorsFilter
17      * @Author zhoum
18      * @Description 解决跨域问题
19      * @Date 17:10 2018-11-22
20      * @Param []
21      **/
22     @Bean
23     public CorsFilter corsFilter() {
24         final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
25         final CorsConfiguration corsConfiguration = new CorsConfiguration();
26         corsConfiguration.setAllowCredentials(true);
27         corsConfiguration.addAllowedOrigin("*");
28         corsConfiguration.addAllowedHeader("*");
29         corsConfiguration.addAllowedMethod("*");
30         urlBasedCorsConfigurationSource.registerCorsConfiguration("/**" , corsConfiguration);
31         return new CorsFilter(urlBasedCorsConfigurationSource);
32     }
33 }

复制代码

  (3)application.yml配置

  

复制代码

 1 spring:2   #色彩日志输出3   output:4     ansi:5       enabled: always6   datasource:7     username: root8     password: 1234569     url: jdbc:mysql://localhost:3306/tjfx?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&allowPublicKeyRetrieval=true
10     type: com.alibaba.druid.pool.DruidDataSource
11     #连接池配置
12     driverClassName: com.mysql.jdbc.Driver
13     # 初始化大小,最小,最大
14     initialSize: 5
15     minIdle: 5
16     maxActive: 20
17     # 配置获取连接等待超时的时间
18     maxWait: 60000
19     # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
20     timeBetweenEvictionRunsMillis: 60000
21     # 配置一个连接在池中最小生存的时间,单位是毫秒
22     minEvictableIdleTimeMillis: 300000
23     validationQuery: SELECT 1 FROM DUAL
24     testWhileIdle: true
25     testOnBorrow: false
26     testOnReturn: false
27     # 打开PSCache,并且指定每个连接上PSCache的大小
28     poolPreparedStatements: true
29     maxPoolPreparedStatementPerConnectionSize: 20
30     # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
31     filters: stat,log4j
32     # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
33     connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
34 
35 mybatis:
36   mapper-locations: classpath*:**/mappers/*.xml
37   type-aliases-package: cn.com.megalith.domain.entity
38   configuration:
39     map-underscore-to-camel-case: true
40     use-generated-keys: true
41     use-column-label: true
42     cache-enabled: true
43     call-setters-on-nulls: true
44 #spring security oauth2配置
45 security:
46   oauth2:
47     #token检查的授权端url
48     authorization:
49       check-token-access: http://127.0.01:8081/oauth/check_token
50     #对应的注册码与注册密码
51     client:
52       id: 1
53       client-secret: 2
54       authentication-scheme: form
55     #获得授权端的当前用户信息url
56     resource:
57       user-info-uri: http://127.0.01:8081/user/me
58       id: oa
59 
60 logging:
61   level:
62     root: info
63     org:
64       springframework: info
65 server:
66   port: 8082

复制代码

  这儿注意http://127.0.01:8081/user/me即授权服务端中的一个接口 需要提供,返回用户信息,代码在授权端项目的UserController下添加如下方法即可

  

复制代码

1     /**
2      *客户端根据token获取用户
3      */
4     @RequestMapping("/me")
5     public Principal user2(OAuth2Authentication principal) {
6         return principal;
7     }

复制代码

 

  (4)启动类配置

复制代码

 1 import org.mybatis.spring.annotation.MapperScan;2 import org.springframework.boot.SpringApplication;3 import org.springframework.boot.autoconfigure.SpringBootApplication;4 5 @SpringBootApplication6 @MapperScan("cn.com.megalith.dao")7 public class CustomerSimpleApplication {8 9     public static void main(String[] args) {
10         SpringApplication.run(CustomerSimpleApplication.class , args);
11     }
12 }

复制代码

 

 2.项目的其他辅助类主要为User的controller,service,dao,entity等  源码里有

 3.项目弄好后结构如下

4.测试

  依旧使用UserController,代码如下

  

复制代码

 1 @RestController2 @RequestMapping("/user")3 public class UserController {4 5     @GetMapping("/current")6     @Secured("ROLE_AA")7     public String getCurrentUser(Principal principal) {8         System.out.println(principal);9         return "111";
10     }
11 }

复制代码

  

  测试方法如下,使用上面获得token的方式获取token后访问如下

  

  注意这儿变成了8082端口,说明是另外的一个资源服务端的项目

  验证成功

  验证失败后返回

  

  如果没有权限则会返回

  

 

到此基于springboot  spring security  +oauth2.0的密码模式授权  且授权端与资源提供端分离的模式就完成了 有问题留言

项目的源码地址为     https://github.com/hetutu5238/zmc_security_oauth2.git           security_simple为认证授权端   customer_simple为资源服务端                 

项目的mysql脚本在   security_simple下的resource/sql/tjfx.sql

这篇关于springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/796528

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory