springboot整合springsecurity+oauth2.0授权认证+jwt增强

springboot整合springsecurity+oauth2.0授权认证+jwt增强

  • 项目整体结构
  • 主要pom文件引入
  • 编写实体类
    • 用户类
    • 角色类
  • 编写自定义的UserDetailSer vice
    • 接口
    • 实现类
  • 编写相关配置文件
    • 强哈希加密
    • jwt增强
    • securityConfig
    • OauthConfig
    • ResourceConfig
  • 测试
    • 使用password授权方式

本文将采用springboot去整合springsecurity,采用oauth2.0授权认证,使用jwt对token增强。本文仅为学习记录,如有不足多谢提出。
一般资源服务器和授权是不放在一起的我为了方便先放在一起,直接在授权处开启资源服务。所有我用授权码模式时会请求不到授权码,其他模式均可获取token并用token访问本项目资源。 后续更新资源服务的配置。

项目整体结构

springboot整合springsecurity+oauth2.0授权认证+jwt增强_第1张图片

主要pom文件引入

<!--        springSecurity-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
<!--        Oauth2-->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
        <!--        jwt增强-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.1.0.RELEASE</version>
        </dependency>

编写实体类

用户类

package com.fxj.springsecurityoauth2.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Collection;
import java.util.List;

@Data
@TableName("sys_user")
public class SysUser implements UserDetails {

    @TableId(type = IdType.AUTO)
    private Long id;

    @TableField("username")
    private String username;

    @TableField("account")
    private String account;

    @TableField("password")
    private String password;

    @TableField(exist = false)
    private List<SysRole> roleList;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roleList;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public String getPassword() {
        return new BCryptPasswordEncoder().encode(password);
    }
}

角色类

package com.fxj.springsecurityoauth2.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;

@TableName("sys_role")
@Data
public class SysRole implements GrantedAuthority {

    @TableId(value = "id",type = IdType.AUTO)
    private Long id;

    @TableField("role_name")
    private String roleName;

    @TableField("role_dec")
    private String roleDec;

    @Override
    public String getAuthority() {
        return roleName;
    }
}

编写自定义的UserDetailSer vice

此处有两种方式第一是在接口中继承UserDetailsService接口,第二种是在service实现类中继承UserDetailsService

接口

public interface UserService extends UserDetailsService {
//    SysUser Login(Principal principal,String userName, String Password) throws HttpRequestMethodNotSupportedException;
}


实现类

//@Service("myService")
@Service
public class UserDetailServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private UserRoleMapper userRoleMapper;


    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        SysUser user = new SysUser();
        List<SysUserRole> userRoles = new ArrayList<>();
        List<SysRole> roles = new ArrayList<>();
        List<Integer> rIds = new ArrayList<>();
        user = userMapper.selectOne(new QueryWrapper<SysUser>().lambda().eq(SysUser::getUsername,s));
        userRoles = userRoleMapper.selectList(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUId,user.getId()));
        userRoles.forEach(a->{
            rIds.add(a.getRId());
        });
        roles = roleMapper.selectList(new QueryWrapper<SysRole>().lambda().in(SysRole::getId,rIds));
        user.setRoleList(roles);
        return user;
    }

//    @Override
//    public SysUser Login(Principal principal,String userName, String password) throws HttpRequestMethodNotSupportedException {
//        SysUser sysUser = new SysUser();
//        sysUser = userMapper.selectOne(new QueryWrapper().lambda()
//                .eq(SysUser::getUsername,userName).eq(SysUser::getPassword,password));
//        return sysUser;
//    }
}

编写相关配置文件

强哈希加密

@Configuration
public class AutoConfig {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
//
//    public AuthenticationManager authenticationManager(){
//        return new AuthenticationManagerBuilder().getObject();
//    }
}

jwt增强

/**
 * JWT内容增强器
 * Created by macro on 2020/6/19.
 */
@Component
public class JwtTokenEnhancer extends JwtAccessTokenConverter {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        SysUser securityUser = (SysUser) authentication.getPrincipal();
        Map<String, Object> info = new HashMap<>();
        //把用户ID设置到JWT中
        info.put("id", securityUser.getId());
//        info.put("client_id",securityUser.getClientId());
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
        return accessToken;
    }
}

securityConfig

package com.fxj.springsecurityoauth2.config;

import com.fxj.springsecurityoauth2.service.UserService;
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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.annotation.Resource;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UserService userDetailService;

    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService).passwordEncoder(passwordEncoder);
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/allow").permitAll()
                .antMatchers("/test/**").authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .httpBasic()
                .and()
                .csrf()
                .disable();
    }
}

OauthConfig

package com.fxj.springsecurityoauth2.config;

import com.fxj.springsecurityoauth2.componet.JwtTokenEnhancer;
import com.fxj.springsecurityoauth2.service.UserService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
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.configuration.EnableResourceServer;
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.TokenEnhancer;
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 org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.security.KeyPair;
import java.util.ArrayList;
import java.util.List;


/**
 * 认证服务配置
 */
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserService userDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("admin-app")
                .secret(passwordEncoder.encode("123456"))
                .scopes("all")
                .authorizedGrantTypes("password", "refresh_token","authorization_code")
                .accessTokenValiditySeconds(3600*24)
                .refreshTokenValiditySeconds(3600*24*7)
                .redirectUris("www.baidu.com")
                .and()
                .withClient("portal-app")
                .secret(passwordEncoder.encode("123456"))
                .scopes("all")
                .authorizedGrantTypes("password", "refresh_token")
                .accessTokenValiditySeconds(3600*24)
                .refreshTokenValiditySeconds(3600*24*7)
                .redirectUris("www.baidu.com");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(accessTokenConverter());
        enhancerChain.setTokenEnhancers(delegates); //配置JWT的内容增强器
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService) //配置加载用户信息的服务
                .accessTokenConverter(accessTokenConverter())
                .tokenEnhancer(enhancerChain)
        .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//        .tokenKeyAccess("permitAll()")
//                .checkTokenAccess("permitAll()")
        security.allowFormAuthenticationForClients();
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //简单对称加密,可使用
        jwtAccessTokenConverter.setSigningKey("123456");
        return jwtAccessTokenConverter;
    }

/**
 * 密钥对jwt.jks为resource下的秘钥对文件(具体生成百度)
 * 此处仅为简单对称加密,无相关文件
 */
//    @Bean
//    public KeyPair keyPair() {
//        //从classpath下的证书中获取秘钥对
//        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray());
//        return keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray());
//    }

}

ResourceConfig

package com.fxj.springsecurityoauth2.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/allow").permitAll()
                .antMatchers("/test/**").authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .httpBasic()
                .and()
                .csrf()
                .disable();
    }
}

测试

使用password授权方式

springboot整合springsecurity+oauth2.0授权认证+jwt增强_第2张图片

  1. grant_type:授权方式
  2. client_id:客户端的名称
  3. client_secret:客户端密码
  4. username:数据库中的账号
  5. password:数据库中的密码

使用token请求受保护的资源
springboot整合springsecurity+oauth2.0授权认证+jwt增强_第3张图片
不受保护的资源
springboot整合springsecurity+oauth2.0授权认证+jwt增强_第4张图片
使用授权码模式测试时不能在一个项目中开启@EnableResourceServer,否则请求不到授权码。

项目地址:https://github.com/fuxiaojunStudy/springsecurity-oauth2.0.git
到此结束。

你可能感兴趣的:(框架使用,jwt,oauth2)