草庐IT

权限管理 springboot集成springSecurity Oauth2 JWT

Dragon Wu 2023-04-22 原文

目录

一、SpringSeurity的基础操作

1、引入主要依赖

2、加密器

3、实现自定义登录逻辑

4、访问限制

5、自定义异常处理 

6、通过注解的方式配置访问控制

二、Auth2认证方案

1、什么是Auth2认证

2、Oauth2最常用的授权模式 

3、依赖引入

4、添加配置类

5、测试

6、存在到Redis里,后续推荐使用JWT

三、JWT认证机制

1、JWT的组成

2、依赖引入

3、生成JWT的测试

4、解析JWT

5、自定义声明

四、SpringSecurityOauth2整合JWT

1、添加配置类

2、Jwt的解析

五、SpringSecurityOauth2实现单点登录

1、添加对应配置


一、SpringSeurity的基础操作

1、引入主要依赖

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

此时我们在启动项目时会发现对于的页面都需要登录才能访问!默认用户名、密码是user,密码是控制台启动时打印的密码。

2、加密器

官方推荐使用BCryptPasswordEncoder这是一个基于Hash单向加密的加密类

    @Test
    public void contextLoads(){
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        //密码加密
        String encode = bCryptPasswordEncoder.encode("123");
        System.out.println(encode);
        System.out.println("==========================");
        //密码匹配,匹配成功返回true,否则返回false
        boolean matches = bCryptPasswordEncoder.matches("123", encode);
        System.out.println(matches);

    }

打印结果:

 在项目使用时一般将其放到Spring容器中

@Configuration
public class SecurityConfig {

    @Bean
    public PasswordEncoder getPw(){
        return new BCryptPasswordEncoder();
    }
}

3、实现自定义登录逻辑

实现SpringSecurity里的UserDetailsService接口的LoadUserByUsername方法便可以实现自定义登录逻辑。以下代码将实现用户名为admin,密码为123的登录。一旦使用了自定义登录逻辑,原本的user和打印的password登录将不再生效。此时,通过admin 123便可登录。

对于登出SpringSecurity也提供了一个/logout的接口。

SpringSecurity框架以为我们提供了一个需要username和passsword参数的登录接口/login 的post请求,需要登录时框架调用的便是该接口。

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询数据库判断用户名是否操作,如果不存在就会抛出UsernameNotFoundException异常
        if (!"admin".equals(username)) {
            throw new UsernameNotFoundException("用户名不存在!");
        }
        //2、把查询出来的密码(注册时已经加密过)进行解析,或者直接把密码放入构造方法
        String password = passwordEncoder.encode("123");
        return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc"));
    }
}

4、访问限制

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder getPw() {
        return new BCryptPasswordEncoder();
    }

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

        //授权认证
        http.authorizeRequests()
                //error.html不需要被认证
                .antMatchers("/error.html").permitAll()
                //login.html不需要被认证
                .antMatchers("/login.html").permitAll()
                //指定角色访问
                .antMatchers("/main.html").hasRole("abc")
                //指定权限访问
                .antMatchers("/main.html").hasAuthority("admin")
                //多个权限都可访问
                .antMatchers("/main.html").hasAnyAuthority("admin,normal")
                //多个角色都可以访问
                .antMatchers("/main.html").hasAnyRole("ABC,abc")
                //通过指定ip地址进行访问,注意这里的ip与localhost转换的ip是不一样的,线上一般为服务器ip
                .antMatchers("/main.html").hasIpAddress("127.0.0.1")
                //所有请求都必须被认证,必须登录之后被访问
                .anyRequest().authenticated();

        //关闭csrf防护
        http.csrf().disable();
    }
}

在访问限制配置时,我们只需要集成WebSecurityConfigurerAdapter配置类configure(HttpSecurity http)方法即可。

多个角色也类似:注意角色这里不要加前缀ROLE_

 对应之前服务实现类的权限和角色:

5、自定义异常处理 

首先添加异常处理类实现AccessDeniedHandler接口的handle方法,handle方法里可自定义处理SpringSecurity的403等异常的处理。

@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        //设置响应状态码
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
        PrintWriter writer = httpServletResponse.getWriter();
        writer.write("{\"status\":\"error\",\"msg\":\"权限不足,请联系管理员\"}");
        writer.flush();
        writer.close();
    }
}

之后在SpringSecurity配置类里添加如下:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //注入异常处理类
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;

    ......

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ......

        //异常处理的配置
        http.exceptionHandling()
                .accessDeniedHandler(myAccessDeniedHandler);
    }
}

当权限不足报403时,这里会调用我们自定义的方法:

6、通过注解的方式配置访问控制

 角色控制访问:注意这里的ROLE_前缀不能去

其他类开启注解@EnableGlobalMethodSecurity(securedEnabled = true)

 控制类里添加@Secured(角色名)注解进行角色访问控制

 当用户有ROLE_abC角色时会被允许访问,否则会报500错误!

使用@PreAuthorize注解来进行访问控制

类似的,启动类里的参数不同而已:

进行角色访问控制:@PreAuthorize("hasRole('abc')")  //此时,有无ROLE_前缀都是可以被访问到的。要注意的时配置类里的hasRole方法是不允许以ROLE_前缀开头的这里有些差别。

同时也可用于权限控制访问:@PreAuthorize("hasAuthority('admin')")

二、Auth2认证方案

1、什么是Auth2认证

2、Oauth2最常用的授权模式 

3、依赖引入

使用Oauth2时我们在SpringSecurity的依赖上添加如下依赖:

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

4、添加配置类

(1)授权模式的配置类如下

SpringSecuirty的配置类:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder getPw() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/oauth/**","/login/**","/logout/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll();
    }
}

Oauth2的授权服务器配置:

@Configuration
@EnableAuthorizationServer  //开启授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
//                .accessTokenValiditySeconds(3600)
                //配置redirect_uri,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型
                .authorizedGrantTypes("authorization_code");

    }
}

Oauth2的资源服务器的配置类:

@Configuration
@EnableResourceServer  //开启资源服务器
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .requestMatchers()
                .antMatchers("/user/**");
    }
}

密码模式的配置类如下:

SpringSecurity配置类:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder getPw() {
        return new BCryptPasswordEncoder();
    }

    //用于密码认证
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception{
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/oauth/**","/login/**","/logout/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll();
    }
}

授权服务器配置:

@Configuration
@EnableAuthorizationServer  //开启授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    //使用密码模式配置所需配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
//                .accessTokenValiditySeconds(3600)
                //配置redirect_uri,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型
                .authorizedGrantTypes("password");

    }
}

资源服务器配置:

与授权获取token时的一样

5、测试

启动服务器

授权模式获取接口资源的步骤

(1)获取授权码:

http://localhost:8080/oauth/authorize?response_type=code&client_id=admin&redirect_uri=http://www.baidu.com&scope=all

选择支持认证。 

认证后记录下这个code里的值:

(2)获取令牌

用户名、密码就是授权服务器里的用户名密码 

填写对应参数:

 发生请求:

(3)获取我们自己写的接口的数据:

 选择Bearer auth认证填入对应token

获取到对应用户信息:

 加密模式获取资源的步骤

(1)通过授权服务获取token

 认证配置与授权时一样

 传入参数有所不同:

通过用户名密码获取到对应token

 (2)获取对应接口资源,与之前授权模式获取接口之源一样

6、存在到Redis里,后续推荐使用JWT

 以上授权的数据是放到内存中的实际开发过程中应将其放到redis里

(1)添加redis依赖与配置

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
spring:
  redis:
    database: 1
    host: my-server
    port: 6380
    password: 

(2)配置类

@Configuration
public class RedisConfig {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public TokenStore redisTokenStore(){
        return new RedisTokenStore(redisConnectionFactory);
    }
}
@Configuration
@EnableAuthorizationServer  //开启授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    @Qualifier("redisTokenStore")
    private TokenStore tokenStore;

    //使用密码模式配置所需配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .tokenStore(tokenStore);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
//                .accessTokenValiditySeconds(3600)
                //配置redirect_uri,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型
                .authorizedGrantTypes("password");

    }
}

放到获取token的接口后可以看到:数据已存在redis中去了

 

三、JWT认证机制

1、JWT的组成

JWT是由.分割的如下三部分组成:

头部(Header)

Header 一般由两个部分组成:

  • alg
  • typ

alg是是所使用的hash算法,如:HMAC SHA256或RSA,typ是Token的类型,在这里就是:JWT。

{
  "alg": "HS256",
  "typ": "JWT"
}

然后使用Base64Url编码成第一部分:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.<second part>.<third part>

载荷(Payload)

这一部分是JWT主要的信息存储部分,其中包含了许多种的声明(claims)。

Claims的实体一般包含用户和一些元数据,这些claims分成三种类型:

  • reserved claims:预定义的 一些声明,并不是强制的但是推荐,它们包括 iss (issuer), exp (expiration time), sub (subject),aud(audience) 等(这里都使用三个字母的原因是保证 JWT 的紧凑)。
  • public claims: 公有声明,这个部分可以随便定义,但是要注意和 IANA JSON Web Token 冲突。
  • private claims: 私有声明,这个部分是共享被认定信息中自定义部分。

一个简单的Pyload可以是这样子的:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

这部分同样使用Base64Url编码成第二部分:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.<third part>

签名(Signature)

Signature是用来验证发送者的JWT的同时也能确保在期间不被篡改。

在创建该部分时候你应该已经有了编码后的Header和Payload,然后使用保存在服务端的秘钥对其签名,一个完整的JWT如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

因此使用JWT具有如下好处:

  • 通用:因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 紧凑:JWT的构成非常简单,字节占用很小,可以通过 GET、POST 等放在 HTTP 的 header 中,非常便于传输。
  • 扩展:JWT是自我包涵的,包含了必要的所有信息,不需要在服务端保存会话信息, 非常易于应用的扩展。

2、依赖引入

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

3、生成JWT的测试

    private void createToken() {
        //当前系统时间
        long now = System.currentTimeMillis();
        //过期时间,1分钟
        long exp = now + 60 * 1000;

        //创建JwtBuilder对象
        JwtBuilder jwtBuilder = Jwts.builder()
                //声明的标识{“jti”:"8888"}
                .setId("8888")
                //主体,用户{“sub”:"Rose"}
                .setSubject("Rose")
                //创建日期{“ita”:“xxxxxx”}
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, "xxxxxx")
                //设置过期时间
                .setExpiration(new Date(exp));
        //获取jwt的token
        String token = jwtBuilder.compact();
        System.out.println(token);

        System.out.println("=======================================");
        String[] split = token.split("\\.");
        System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
        System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
        //无法解密
        System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
    }

打印如下:密码盐一般为服务器的私钥

4、解析JWT

    private void parseJWT() {
        String jwt = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTY2NTAzNzc0MSwiZXhwIjoxNjY1MDM3ODAxfQ.jGZrGW0sYyfuatsi1xtYXI8pPflRZR4DY3BbwmSKN0M";
        //解析token获取负载中声明的对象
        Claims claims = Jwts.parser()
                .setSigningKey("xxxxxx")
                .parseClaimsJws(jwt)
                .getBody();
        System.out.println("id:" + claims.getId());
        System.out.println("subject:" + claims.getSubject());
        System.out.println("issuedAt:" + claims.getIssuedAt());
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("签发时间:" + simpleDateFormat.format(claims.getIssuedAt()));
        System.out.println("过期时间:" + simpleDateFormat.format(claims.getExpiration()));
        System.out.println("当前时间:" + simpleDateFormat.format(new Date()));
    }

 

5、自定义声明

    //自定义声明
    private void createJWTByClaims() {
        //当前系统时间
        long now = System.currentTimeMillis();
        //过期时间,1分钟
        long exp = now + 60 * 1000;

        //创建JwtBuilder对象
        JwtBuilder jwtBuilder = Jwts.builder()
                //声明的标识{“jti”:"8888"}
                .setId("8888")
                //主体,用户{“sub”:"Rose"}
                .setSubject("Rose")
                //创建日期{“ita”:“xxxxxx”}
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, "xxxxxx")
                //设置过期时间
                .setExpiration(new Date(exp))
                //自定义声明的使用
                .claim("roles", "admin")
                .claim("logo", "xxx.jpg");
        //直接传入Map
//                .setClaims(map)
        //获取jwt的token
        String token = jwtBuilder.compact();
        System.out.println(token);
    }

解析:

private void parseJWTClaims() {
        String jwt = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTY2NTAzODQwMiwiZXhwIjoxNjY1MDM4NDYxLCJyb2xlcyI6ImFkbWluIiwibG9nbyI6Inh4eC5qcGcifQ.fzyWQ_HG725kDoZ0zljHkIiIrO1mhl_u77r81ZeT1FA";
        //解析token获取负载中声明的对象
        Claims claims = Jwts.parser()
                .setSigningKey("xxxxxx")
                .parseClaimsJws(jwt)
                .getBody();
        System.out.println("id:" + claims.getId());
        System.out.println("subject:" + claims.getSubject());
        System.out.println("issuedAt:" + claims.getIssuedAt());
        System.out.println("roles:" + claims.get("roles"));
        System.out.println("logo:" + claims.get("logo"));
    }

四、SpringSecurityOauth2整合JWT

1、添加配置类

Jwt的配置类

@Configuration
public class JwtTokenStoreConfig {

    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        //配置jwt使用的密钥
        accessTokenConverter.setSigningKey("test_key");
        return accessTokenConverter;
    }

    @Bean
    public JwtTokenEnhancer jwtTokenEnhancer(){
        return new JwtTokenEnhancer();
    }
}

Jwt内容增强器的配置类:

public class JwtTokenEnhancer implements TokenEnhancer {


    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
        Map<String, Object> info = new HashMap<>();
        //放入jwt里的内容
        info.put("enhance", "enhance info");
        ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info);
        return oAuth2AccessToken;
    }
}

授权服务器配置类:

@Configuration
@EnableAuthorizationServer  //开启授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    @Qualifier("jwtTokenStore")
    private TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    //使用密码模式配置所需配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //配置JWT内容增强器
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(delegates);

        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                //配置存储令牌策略
                .tokenStore(tokenStore)
                .accessTokenConverter(jwtAccessTokenConverter)
                //配置内容增强器
                .tokenEnhancer(enhancerChain);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
                .accessTokenValiditySeconds(3600)
                //配置刷新令牌的有效期
                .refreshTokenValiditySeconds(864000)
                //配置redirect_uri,用于授权成功后跳转
                .redirectUris("http://localhost:8081/login")
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型, 刷新令牌
                .authorizedGrantTypes("password","refresh_token");
    }
}

重启项目:

使用正确密码模式的访问接口进行测试:可以看到目前的token已经是JWT的token了

 将此token放到官网解析可以看到对应增加的信息:

2、Jwt的解析

这里需要引入之前集成JWT的依赖

添加访问接口进行测试

    @GetMapping("/getCurrentUser")
    public Object getCurrentUser(Authentication authentication, HttpServletRequest request) {
        String head = request.getHeader("Authorization");
        String token = head.substring(head.indexOf("bearer") + 7);
        return Jwts.parser()
                .setSigningKey("test_key".getBytes(StandardCharsets.UTF_8))
                .parseClaimsJws(token)
                .getBody();
    }

访问该接口,仅需传入请求头Authorization和对应的bearer+JWT即可:

 解析完毕:

3、刷新token

授权服务器配置如下:

 再次测试:可以看到刷新时的token

五、SpringSecurityOauth2实现单点登录

我以之前的授权服务器作为授权服务器,现在在创建一个服务器作为单点请求登录的服务器客户端。依赖与授权服务器相同。

1、添加对应配置

server:
  port: 8081
  servlet:
    session:
      # 防止Cookie冲突,冲突会导致登录验证不通过
      cookie:
        name: OAUTH2-CLIENT-SESSIONID01
# 授权服务器地址
oauth2-server-url: http://localhost:8080
# 与授权服务器对应的配置
security:
  oauth2:
    client:
      client-id: admin
      client-secret: 112233
      user-authorization-uri: ${oauth2-server-url}/oauth/authorize
      access-token-uri: ${oauth2-server-url}/oauth/token
    resource:
      jwt:
        key-uri: ${oauth2-server-url}/oauth/token_key

使用单点登录时授权服务器的授权配置类: 

@Configuration
@EnableAuthorizationServer  //开启授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    @Qualifier("jwtTokenStore")
    private TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    //使用密码模式配置所需配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //配置JWT内容增强器
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(delegates);

        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                //配置存储令牌策略
                .tokenStore(tokenStore)
                .accessTokenConverter(jwtAccessTokenConverter)
                //配置内容增强器
                .tokenEnhancer(enhancerChain);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
                .accessTokenValiditySeconds(3600)
                //配置刷新令牌的有效期
                .refreshTokenValiditySeconds(864000)
                //配置redirect_uri,用于授权成功后跳转
                .redirectUris("http://localhost:8081/login")
                //自动授权配置
                .autoApprove(true)
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型, 刷新令牌
                .authorizedGrantTypes("password", "refresh_token", "authorization_code");
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //获取密钥需要身份认证,使用单点登录时必须配置
        security.tokenKeyAccess("isAuthenticated()");
    }
}

 访问客户端的接口:

http://localhost:8081/user/getCurrentUser

此时,会跳转到授权服务器的登录页面:

登录成功访问到对应接口资源:

 

总结到此。

有关权限管理 springboot集成springSecurity Oauth2 JWT的更多相关文章

  1. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  2. ruby-on-rails - 如何使辅助方法在 Rails 集成测试中可用? - 2

    我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel

  3. ruby-on-rails - 我如何将 Hoptoad 与 DelayedJob 和 DaemonSpawn 集成? - 2

    我一直很高兴地使用DelayedJob习惯用法:foo.send_later(:bar)这会调用DelayedJob进程中对象foo的方法bar。我一直在使用DaemonSpawn在我的服务器上启动DelayedJob进程。但是...如果foo抛出异常,Hoptoad不会捕获它。这是任何这些包中的错误...还是我需要更改某些配置...或者我是否需要在DS或DJ中插入一些异常处理来调用Hoptoad通知程序?回应下面的第一条评论。classDelayedJobWorker 最佳答案 尝试monkeypatchingDelayed::W

  4. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  5. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  6. jenkins部署1--jenkins+gitee持续集成 - 2

    前置步骤我们都操作完了,这篇开始介绍jenkins的集成。话不多说,看操作1、登录进入jenkins后会让你选择安装插件,选择第一个默认的就行。安装完成后设置账号密码,重新登录。2、配置JDK和Git都需要执行路径,所以需要先把执行路径找到,先进入服务器的docker容器,2.1JDK的路径root@69eef9ee86cf:/usr/bin#echo$JAVA_HOME/usr/local/openjdk-82.2Git的路径root@69eef9ee86cf:/#whichgit/usr/bin/git3、先配置JDK和Git。点击:ManageJenkins>>GlobalToolCon

  7. ruby - rbenv 安装权限被拒绝 - 2

    大家好,我正在尝试设置一个开发环境,并且我一直在关注以下教程:Linktotutorial我做得不是很好,除了最基本的版本控制内容外,我对终端命令没有任何实际经验。我点击了第一个链接并尝试运行source~/.bash_profile我得到了错误;mkdir:/usr/local/rbenv/shims:权限被拒绝mkdir:/usr/local/rbenv/versions:权限被拒绝现在每次我加载终端时都会出现错误。bash_profile的内容;exportPATH=/usr/local/rbenv/bin:$PATHexportRBENV_ROOT=/usr/local/rbe

  8. ruby - (Ruby || Python) 窗口管理器 - 2

    我想用这两种语言中的任何一种(最好是ruby​​)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生

  9. ruby-on-rails - 事件管理员和自定义方法 - 2

    这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什

  10. ruby-on-rails - (Ruby,Rails) 基于角色的身份验证和用户管理...? - 2

    我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源

随机推荐