zoukankan      html  css  js  c++  java
  • 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: zhoum
     26  * @Date: 2018-11-22
     27  * @Time: 13:41
     28  */
     29 @Configuration
     30 @EnableAuthorizationServer //授权认证中心
     31 public class CustomAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
     32 
     33     /**
     34      * 权限验证控制器
     35      */
     36     @Autowired
     37     private AuthenticationManager authenticationManager;
     38     /**
     39      * 数据源,保存token的时候需要 默认为spring中配置的datasource
     40      */
     41     @Autowired
     42     private DataSource dataSource;
     43     /**
     44      * 设置保存token的方式,一共有五种,这里采用数据库的方式
     45      */
     46     @Autowired
     47     private TokenStore tokenStore;
     48 
     49     @Bean
     50     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     @Override
     68     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     @Override
     81     public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
     82         /**
     83          * 配置oauth2服务跨域
     84          */
     85         CorsConfigurationSource source = new CorsConfigurationSource() {
     86             @Override
     87             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: zhoum
     9  * @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: zhoum
     14  * @Date: 2018-11-22
     15  * @Time: 15:46
     16  */
     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     @Override
     36     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     @Override
     49     public String getPassword() {
     50         return user.getPassword();
     51     }
     52     /**
     53      * 获取用户名
     54      */
     55     @Override
     56     public String getUsername() {
     57         return user.getUsername();
     58     }
     59     /**
     60      * 账户是否未过期
     61      */
     62     @Override
     63     public boolean isAccountNonExpired() {
     64         return true;
     65     }
     66     /**
     67      * 账户是否未锁定
     68      */
     69     @Override
     70     public boolean isAccountNonLocked() {
     71         return true;
     72     }
     73     /**
     74      * 证书是否未过期
     75      */
     76     @Override
     77     public boolean isCredentialsNonExpired() {
     78         return true;
     79     }
     80 
     81     /**
     82      * 是否有效   可对应数据库中的delete_flag字段
     83      */
     84     @Override
     85     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: always
      datasource:
        username: root
        password: 123456
        url: jdbc:mysql://localhost:3306/tjfx?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&allowPublicKeyRetrieval=true
        type: com.alibaba.druid.pool.DruidDataSource
        #连接池配置
        driverClassName: com.mysql.jdbc.Driver
        # 初始化大小,最小,最大
        initialSize: 5
        minIdle: 5
        maxActive: 20
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        # 打开PSCache,并且指定每个连接上PSCache的大小
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
        filters: stat,log4j
        # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    
    mybatis:
      mapper-locations: classpath*:**/mappers/*.xml
      type-aliases-package: cn.com.megalith.domain.entity
      configuration:
        map-underscore-to-camel-case: true
        use-generated-keys: true
        use-column-label: true
        cache-enabled: true
        call-setters-on-nulls: true
    logging:
      level:
        root: info
        org:
          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 @SpringBootApplication
     7 @MapperScan("cn.com.megalith.dao")
     8 @EnableTransactionManagement
     9 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 @RestController
     2 @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: always
     6   datasource:
     7     username: root
     8     password: 123456
     9     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 @SpringBootApplication
     6 @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 @RestController
     2 @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

     

    在获取token时  如何返回如下格式的数据结构?

    1.定义返回类

     1 public class Result<T> implements Serializable {
     2 
     3     private String message ;
     4 
     5     private Integer code ;
     6 
     7     private T result;
     8 
     9 
    10     public static<T> Result<T> ok(T data) {
    11         Result<T> r = new Result<>();
    12         r.setMessage("ok");
    13         r.setCode(HttpStatus.OK.value());
    14         r.setResult(data);
    15         return r;
    16     }
    17 
    18     public String getMessage() {
    19         return message;
    20     }
    21 
    22     public void setMessage(String message) {
    23         this.message = message;
    24     }
    25 
    26     public Integer getCode() {
    27         return code;
    28     }
    29 
    30     public void setCode(Integer code) {
    31         this.code = code;
    32     }
    33 
    34     public T getResult() {
    35         return result;
    36     }
    37 
    38     public void setResult(T result) {
    39         this.result = result;
    40     }
    41 }

    2.pom.xml新增

    1         <dependency>
    2             <groupId>org.springframework.boot</groupId>
    3             <artifactId>spring-boot-starter-aop</artifactId>
    4         </dependency>

    3.增加配置类   项目源码我注掉了  有需要的可以打开

     1 @Aspect
     2 @Component
     3 public class CodeAspect {
     4 
     5     @Pointcut("execution(public * org.springframework.security.oauth2.provider..*.*TokenEndpoint.postAccessToken(..))")
     6     public void excudeService() {
     7     }
     8 
     9     @Around("excudeService()")
    10     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
    11 
    12         Object result = pjp.proceed();
    13         if ( result instanceof ResponseEntity ){
    14             ResponseEntity res = (ResponseEntity)result;
    15             if ( res.getStatusCode() .equals(HttpStatus.OK) ){
    16                 Object body = res.getBody();
    17                 if ( body instanceof DefaultOAuth2AccessToken ){
    18                     DefaultOAuth2AccessToken data = (DefaultOAuth2AccessToken)body;
    19                     ResponseEntity codeResult = new ResponseEntity(Result.ok(data.getValue()),HttpStatus.OK);
    20                     return codeResult;
    21                 }
    22             }
    23 
    24         }
    25         return result;
    26     }
    27 }

    这样即可

  • 相关阅读:
    linux常用命令---文件操作
    Django 框架中定时触发脚本
    jquery 中选择当前标签下众多相同子标签中的第n个
    Django + DRF + Elasticsearch 实现搜索功能
    django 使用celery 实现异步任务
    python 通过 pymysql模块 操作 mysql 数据库
    django 自定义中间件 middleware
    django 使用其自带的验证系统 进行用户名有效性验证 登录状态验证 登入操作 登出操作
    python脚本 读取excel格式文件 并进行处理的方法
    python 将json格式的数据写入csv格式的文件中
  • 原文地址:https://www.cnblogs.com/hetutu-5238/p/10022963.html
Copyright © 2011-2022 走看看