oauth2.0系统学习-spring sercurity+oauth2.0+JWT 单点登陆设计

oauth2.0系统学习-spring sercurity+oauth2.0+JWT 单点登陆设计


一、spring sercurity+oauth2.0+JWT 单点登陆设计

  1. 认证服务器和资源服务器在同一台机器(网关)
  2. 密码授权模式
    1. 去掉客户端信息表
    2. 去掉用户授权表

在这里插入图片描述

pom.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>oauth-learn2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>oauth-learn2</name>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--security    end-->

        <!--数据库依赖    begin-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--数据库依赖    end-->


        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.0.10.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.properties

spring.application.name=oauth-authorizationServer
server.port=8083

spring.datasource.url = jdbc:mysql://localhost:3306/testoauth?useUnicode=true
spring.datasource.username=root
spring.datasource.password=ylx123
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.main.allow-bean-definition-overriding=true

UserBean

package com.example.oauthlearn2.bean;

import lombok.Data;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/25  11:13
 * @Verson 1.0
 **/

@Data
public class UserBean {
    private Long id;
    private String userName;
    private String password;

}

AuthorizationServer

package com.example.oauthlearn2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.Arrays;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/25  11:14
 * @Verson 1.0
 **/

@Configuration
@EnableAuthorizationServer // 配置授权服务。
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;
    @Autowired
    // 授权码服务
    private AuthorizationCodeServices authorizationCodeServices;
    @Autowired
    // 认证管理
    private AuthenticationManager authenticationManager;
    @Autowired
    // 令牌管理服务
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    @Autowired
    // 令牌存储策略
    private TokenStore tokenStore;
    @Autowired
    // 客户端详情服务,也就是configure(ClientDetailsServiceConfigurer clients)方法
    private ClientDetailsService clientDetailsService;

    @Autowired
    // 加密方式
    private PasswordEncoder passwordEncoder;

    // 用来配置客户端详情服务
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 这里是第三方合作用户的客户id,秘钥的配置
        // 使用in-memory存储
        clients.inMemory()
                // client_id,用户账号
                .withClient(OauthCanstant.clientId)
                // 客户端密钥
                .secret(new BCryptPasswordEncoder().encode(OauthCanstant.secret))
                // 资源列表,资源标识
                .resourceIds(OauthCanstant.resourceIds)
                // 授权类型(4种)
                .authorizedGrantTypes(OauthCanstant.authorizedGrantTypes)
                // 客戶端允许的授权范围
                .scopes(OauthCanstant.scopes)
                // false跳转到授权页面,让用户点击授权,如果是true,相当于自动点击授权,就不跳转授权页面
                .autoApprove(OauthCanstant.autoApprove)//
                // 加上验证回调地址,返回授权码信息
                .redirectUris(OauthCanstant.redirectUris);
    }

    // 令牌管理服务
    @Bean
    public AuthorizationServerTokenServices authorizationServerTokenServices() {
        DefaultTokenServices service = new DefaultTokenServices();
        service.setClientDetailsService(clientDetailsService);
        service.setSupportRefreshToken(true);// 支持刷新
        service.setTokenStore(tokenStore);// 令牌存储

        // 令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
        service.setTokenEnhancer(tokenEnhancerChain);

        service.setAccessTokenValiditySeconds(OauthCanstant.accessTokenValiditySeconds); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(OauthCanstant.refreshTokenValiditySeconds); // 刷新令牌默认有效期3天
        return service;
    }

    @Override
    // 用来配置令牌(token)的访问端点
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                // 指定认证管理器
                .authenticationManager(authenticationManager)
                // 授权码模式需要
                .authorizationCodeServices(authorizationCodeServices)
                // 令牌管理服务
                .tokenServices(authorizationServerTokenServices)
                // jwt格式Token
                .accessTokenConverter(accessTokenConverter)
                // 允许post提交
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    @Bean
    // 授权码服务器
    public AuthorizationCodeServices authorizationCodeServices() {
        // 授权码模式的授权码采用内存方式存储
        return new InMemoryAuthorizationCodeServices();
    }
    @Override
    // 用来配置令牌端点的安全约束,拦截规则
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

        security
                // 提供公有密匙的端点,如果你使用JWT令牌的话, 允许
                .tokenKeyAccess(OauthCanstant.permitAll)
                // oauth/check_token:用于资源服务访问的令牌解析端点,允许
                .checkTokenAccess(OauthCanstant.permitAll)
                // 表单认证,申请令牌
                .allowFormAuthenticationForClients();
    }

}

OauthCanstant

package com.example.oauthlearn2.config;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/25  11:15
 * @Verson 1.0
 **/

public interface OauthCanstant {
    String clientId = "c1";
    String secret = "secret";
    String resourceIds = "res1";
    String authorizedGrantTypes = "password";
    String scopes = "all";
    boolean autoApprove = false;
    String redirectUris = "http://www.baidu.com";
    String permitAll = "permitAll()";
    // 令牌默认有效期2小时
    int accessTokenValiditySeconds = 7200;
    // 刷新令牌默认有效期3天
    int refreshTokenValiditySeconds = 259200;

    // 对称秘钥,资源服务器使用该秘钥来验证
    String SIGNING_KEY = "uaa123";

}

ResouceServerConfig

package com.example.oauthlearn2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/25  11:15
 * @Verson 1.0
 **/

@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    // 令牌存储策略
    private TokenStore tokenStore;



    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources
                // 资源 id
                .resourceId(OauthCanstant.resourceIds)
                //                // 令牌服务
                .tokenStore(tokenStore)
                .stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http

                .authorizeRequests().antMatchers("/**")
                // 所有的访问,授权访问都要是all,和认证服务器的授权范围一一对应
                .access("#oauth2.hasScope('"+OauthCanstant.scopes+"')")
                //去掉防跨域攻击
                .and().csrf().disable()
                //session管理
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }


}

TokenConfig

package com.example.oauthlearn2.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/25  11:16
 * @Verson 1.0
 **/

@Configuration
public class TokenConfig {


    @Bean
    public TokenStore tokenStore() {
        // JWT令牌存储方案
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(OauthCanstant.SIGNING_KEY); // 对称秘钥,资源服务器使用该秘钥来验证
        return converter;
    }

}

WebSecurityConfig

package com.example.oauthlearn2.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/25  11:16
 * @Verson 1.0
 **/

@Configuration
// @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // 认证管理器
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    // 密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 安全拦截机制
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests().anyRequest().authenticated()// 所有其他请求必须认证通过
                .and().formLogin().loginPage("/login").successForwardUrl("/login-success")// 自定义登录成功的页面地址
                .permitAll().and().logout().permitAll().and()// 会话管理
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);// 会话管理

    }
}

LoginController

package com.example.oauthlearn2.controller;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/25  11:17
 * @Verson 1.0
 **/

@RestController
public class LoginController {

    @RequestMapping(value = "/login-success")
    public String loginSuccess() {
        return getUsername() + " login-success 登录成功";
    }

    /**
     * 测试资源1
     *
     * @return
     */
    @GetMapping(value = "/admin/p1")
    // @PreAuthorize("hasAuthority('p1')")//拥有p1权限才可以访问
    public String r1() {
        return " /admin/p1 " + getUsername() + "访问资源1";
    }

    /**
     * 测试资源2
     *
     * @return
     */
    @GetMapping(value = "/user/p2")
    // @PreAuthorize("hasAuthority('p2')")//拥有p2权限才可以访问
    public String r2() {
        return "/user/p2 " + getUsername() + "访问资源2";
    }

    // 获取当前用户信息
    private String getUsername() {
        String username = null;
        // 当前认证通过的用户身份
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        // 用户身份
        Object principal = authentication.getPrincipal();
        if (principal == null) {
            username = "匿名";
        }
        if (principal instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) principal;
            username = userDetails.getUsername();
        } else {
            username = principal.toString();
        }
        return username;
    }
}

UserDao

package com.example.oauthlearn2.dao;

import com.example.oauthlearn2.bean.UserBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/25  11:17
 * @Verson 1.0
 **/

@Repository
public class UserDao {
    @Autowired
    JdbcTemplate jdbcTemplate;

    // 根据账号查询用户信息
    public UserBean getUserByUsername(String username) {
        String sql = "select * from user where user_name = ?";
        // 连接数据库查询用户
        List<UserBean> list = jdbcTemplate.query(sql, new Object[] { username },
                new BeanPropertyRowMapper<>(UserBean.class));
        if (list != null && list.size() == 1) {
            return list.get(0);
        }
        return null;
    }

}

SpringDataUserDetailsService

package com.example.oauthlearn2.service;

import com.example.oauthlearn2.bean.UserBean;
import com.example.oauthlearn2.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
 * @Description 请描述下该类是做什么的
 * @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
 * @Date 2021/9/25  11:18
 * @Verson 1.0
 **/

@Service
public class SpringDataUserDetailsService implements UserDetailsService {

    @Autowired
    UserDao userDao;

    // 根据账号查询用户信息,
    // 通过@Service将SpringDataUserDetailsService注入容器,通过UserDetailsService接口表明该类的类型是UserDetailsService
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 将来连接数据库根据账号查询用户信息
        UserBean bean = userDao.getUserByUsername(username);
        if (bean == null) {
            // 如果用户查不到,返回null,由provider来抛出异常
            return null;
        }


        // 添加p1权限,权限是随便加的,这里已经没有意义了
        UserDetails userDetails = User.withUsername(bean.getUserName()).password(bean.getPassword()).authorities("p1")
                .build();
        return userDetails;
    }
}

测试

获取admin的token

http://localhost:8083/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=admin&password=admin

在这里插入图片描述

admin请求可以访问的资源:

http://localhost:8083/admin/p1

头部信息中添加 Authorization,值为:“Bearer ” + token

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值