springboot+oauth2+redis实现单点登录

写这篇博客的时候看了网上很多Oauth2的案例,很多都集成了jwt,但是感觉这个东西实在是没有必要。

以下是自己参考网上博客,琢磨出来的代码,有待完善。

github:https://github.com/LI-DAI/mall_demo

mall-admin 模块

首先贴以下主要依赖



  org.springframework.boot
  spring-boot-starter-security



  org.springframework.security.oauth.boot
  spring-security-oauth2-autoconfigure
  2.1.3.RELEASE

再看一下配置文件

server.port=2001
spring.application.name=mall-admin
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://10.0.93.60:3306/mall_demo?useUnicode=true&characterEncoding=utf8
#spring.datasource.druid.url=jdbc:mysql://localhost:3306/mall_demo?useUnicode=true&characterEncoding=utf8
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
#\u521D\u59CB\u5316\u8FDE\u63A5\u6C60\u5927\u5C0F
spring.datasource.druid.initial-size=1
#\u6700\u5927\u8FDE\u63A5
spring.datasource.druid.max-active=20
#\u7B49\u5F85\u8D85\u65F6\u65F6\u95F4
spring.datasource.druid.max-wait=30000
#\u6700\u5C0F\u8FDE\u63A5
spring.datasource.druid.min-idle=1
#\u9694\u591A\u4E45\u8FDB\u884C\u4E00\u6B21\u68C0\u6D4B \u68C0\u6D4B\u9700\u8981\u5173\u95ED\u7A7A\u95F2\u8FDE\u63A5 \u5355\u4F4D\u6BEB\u79D2
spring.datasource.druid.time-between-eviction-runs-millis=50000
#\u4E00\u4E2A\u8FDE\u63A5\u5728\u6C60\u4E2D\u6700\u5C0F\u7684\u751F\u5B58\u65F6\u95F4 \u5355\u4F4D\u6BEB\u79D2
spring.datasource.druid.min-evictable-idle-time-millis=50000
#\u9A8C\u8BC1\u8FDE\u63A5\u662F\u5426\u6709\u6548
spring.datasource.druid.validation-query=select 1 from dual
#\u5982\u679C\u7A7A\u95F2\u65F6\u95F4\u5927\u4E8EtimeBetweenEvictionRunsMillis\uFF0C\u5219\u6267\u884Cvalidation-query\u68C0\u6D4B\u662F\u5426\u6709\u6548
spring.datasource.druid.test-while-idle=true
#\u7533\u8BF7\u8FDE\u63A5\u65F6\u662F\u5426\u68C0\u6D4B
spring.datasource.druid.test-on-borrow=false
#\u5F52\u8FD8\u8FDE\u63A5\u65F6\u662F\u5426\u68C0\u6D4B
spring.datasource.druid.test-on-return=false
#\u6253\u5F00PsCache
spring.datasource.druid.pool-prepared-statements=true
#\u6307\u5B9APsCache\u5927\u5C0F
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
#\u914D\u7F6E\u76D1\u63A7\u7EDF\u8BA1\u62E6\u622A\u7684filters \u82E5\u4E0D\u914D\u7F6E \u65E0\u6CD5\u7EDF\u8BA1\u76D1\u63A7\u754C\u9762sql
spring.datasource.druid.filters=stat,wall
#\u5408\u5E76\u591A\u4E2ADruidDataSource\u7684\u76D1\u63A7\u6570\u636E
spring.datasource.druid.use-global-data-source-stat=true
#\u901A\u8FC7connectProperties\u5C5E\u6027\u6765\u6253\u5F00mergeSql\u529F\u80FD\uFF1B\u6162SQL\u8BB0\u5F55
spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
#JPA
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
#redis
spring.redis.database=0
spring.redis.port=6379
spring.redis.host=10.0.93.60
spring.redis.password=
spring.redis.timeout=5000ms
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.max-active=8
#\u6587\u4EF6\u4E0A\u4F20\u8DEF\u5F84
upload.locations=d:/test
#security
#spring.security.user.name=root
#spring.security.user.password=123456
auth-server=http://localhost:2001/oauth
security.oauth2.client.client-id=client
security.oauth2.client.client-secret=secret
security.oauth2.client.access-token-uri=${auth-server}/token
security.oauth2.client.user-authorization-uri=${auth-server}/authorize

下面就是重点了

SecurityConfiguration.java


package com.mall.admin.configuration;

import com.mall.admin.entity.Permission;
import com.mall.admin.entity.User;
import com.mall.admin.repository.PermissionRepository;
import com.mall.admin.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author lidai
 * @date 2019/7/9 11:06
 * @since
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PermissionRepository permissionRepository;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(userDetailsService()).passwordEncoder(bCryptPasswordEncoder());
    }


    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            User user = userRepository.findByUsername(username);
            if (null == user) {
                throw new IllegalArgumentException("Username : " + username + " not found !");
            }
            List permissions = permissionRepository.getPermissionsByUserId(user.getUserId());
            Set perms = permissions.stream().map(Permission::getPerms).collect(Collectors.toSet());
            List authorities = AuthorityUtils.createAuthorityList(perms.toArray(new String[]{}));
            return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
        };
    }


    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }


//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        http.formLogin()
//                .successHandler((request, response, authentication) -> response.getWriter().write("congratulations ! login success ."))
//                .and()
//                .authorizeRequests()
//                .antMatchers("/login").permitAll()
//                .antMatchers(HttpMethod.OPTIONS).permitAll()
//                .anyRequest().authenticated()
//                .and()
//                .exceptionHandling()
//                .accessDeniedHandler((request, response, accessDeniedException) -> {
//                    response.setCharacterEncoding("UTF-8");
//                    Writer writer = response.getWriter();
//                    writer.write(JSONObject.toJSONString(Result.build().unauthorized()));
//                    writer.flush();
//                });
//
//    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/oauth/check_token", "/user/login");
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

AuthorizationServerConfiguration.java
/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.mall.admin.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.sql.DataSource;

/**
 * @author lidai
 * @date 2019/7/9 14:16
 * 

* 认证服务器 */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Autowired private BCryptPasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private DataSource dataSource; @Autowired private RedisConnectionFactory redisConnectionFactory; @Autowired private UserDetailsService userDetailsService; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //自动从数据库查数据 // clients.withClientDetails(detailsService()); clients.inMemory() .withClient("browser") .secret(passwordEncoder.encode("secret")) .authorizedGrantTypes("password", "authorization_code", "refresh_token") .scopes("all"); // 注册回调地址 // .redirectUris("http://www.funtl.com"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore()) .authenticationManager(authenticationManager).userDetailsService(userDetailsService); // DefaultTokenServices tokenServices = new DefaultTokenServices(); // tokenServices.setTokenStore(endpoints.getTokenStore()); // tokenServices.setSupportRefreshToken(true); // tokenServices.setClientDetailsService(endpoints.getClientDetailsService()); // tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); // //1小时 // tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.HOURS.toSeconds(2)); // //1天 // tokenServices.setRefreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(1)); // tokenServices.setReuseRefreshToken(false); // endpoints.tokenServices(tokenServices); } @Bean public TokenStore tokenStore() { // return new JdbcTokenStore(dataSource); return new RedisTokenStore(redisConnectionFactory); } // @Bean // public ClientDetailsService detailsService() { // return new JdbcClientDetailsService(dataSource); // } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()"); } }

ResourceServerConfiguration.java
/*
 * Copyright (C), 2013-2019, 天津大海云科技有限公司
 */
package com.mall.admin.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

/**
 * @author lidai
 * @date 2019/7/10 15:53
 * @since
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        super.configure(resources);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                //请求权限配置
                .authorizeRequests()
                .antMatchers("/user/login").permitAll()
                .antMatchers("/oauth/*").permitAll()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .anyRequest().authenticated();
    }
}

配置类就这样了,代码写的不是太好,将就看吧,以后还会不断完善填充

下面是另一个模块

mall-market 模块

这个模块只有一资源服务器,访问的时候会直接请求到认证服务器验证是否登录

配置文件

server:
  port: 2002

spring:
  application:
    name: mall-market
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://10.0.93.60:3306/mall_demo?useUnicode=true&characterEncoding=utf8
#      url: jdbc:mysql://localhost:3306/mall_demo?useUnicode=true&characterEncoding=utf8
      username: root
      password: 123456
      initial-size: 1
      max-active: 20
      max-wait: 30000
      min-idle: 1
      time-between-eviction-runs-millis: 50000
      min-evictable-idle-time-millis: 50000
      validation-query: select 1 from dual
      test-on-borrow: false
      test-on-return: false
      test-while-idle: true
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      filter: stat,wall
      use-global-data-source-stat: true
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  jpa:
    database: mysql
    show-sql: true
    hibernate:
      ddl-auto: update
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
  redis:
    database: 0
    port: 6379
    host: 10.0.93.60
    password:
    timeout: 5000ms
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: -1ms
        min-idle: 1
#security
auth-server: http://localhost:2001/oauth
security:
  oauth2:
    client:
      client-id: browser
      client-secret: secret
      access-token-uri: ${auth-server}/token
      user-authorization-uri: ${auth-server}/authorize
    resource:
      token-info-uri: ${auth-server}/check_token
ResourceServerConfiguration.java

package com.mall.market.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

/**
 * @author lidai
 * @date 2019/7/10 15:53
 * @since
 */
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        super.configure(resources);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                //请求权限配置
                .authorizeRequests()
                .antMatchers("/user/login").permitAll()
                .antMatchers("/oauth/*").permitAll()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .anyRequest().authenticated();
    }
}

postman 测试

可以直接访问 http://localhost:2001/oauth/token 获取token

添加Authorization 参数,即为我们定义再内存中的client与secret

springboot+oauth2+redis实现单点登录_第1张图片

springboot+oauth2+redis实现单点登录_第2张图片

获取成功之后,我们看看redis中的数据

springboot+oauth2+redis实现单点登录_第3张图片

我使用了两个用户进行登录,分别是user和admin

成功获取token之后,可以使用此token进行访问

示例,访问market模块下接口

 springboot+oauth2+redis实现单点登录_第4张图片

这里需要注意,需要再access_token前加上token类型:bearer

到这里基本上单点登录的功能就已经实现,若有错误,欢迎指正。  

你可能感兴趣的:(oauth2)