springboot、Security、Oauth2、JWT实现权限认证

前言:
权限认证授权,是一个系统最基础,也是最重要的一部分。网上教程五花八门,没有找到一篇能够完整的描述正确,这里本人也是搜集资料,实战编写了管理系统的权限认证授权模块,话不多说,看官们请往下看。

1,权限认证授权的定义

定义略,百度一下。

2,搭建权限模块的流程

1,导入依赖包

本项目是使用gradle搭建的,maven项目添加对应的包即可。

compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.security.oauth:spring-security-oauth2:2.3.5.RELEASE')
compile('org.springframework.security:spring-security-jwt:1.1.0.RELEASE')
compile('io.jsonwebtoken:jjwt:0.9.0')
compile('com.auth0:java-jwt:3.5.0')

2,三个核心配置文件类

一般会创建config文件夹,存放一些配置文件。这里需要这三个配置类,AuthorizationServerConfiguration认证服务,OAuth2SecurityConfiguration(security配置),ResourceServerConfiguration资源服务。以下把源代码放出来。

在这里插入图片描述

AuthorizationServerConfiguration:

package com.generic.admin_scaffolding.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.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.approval.UserApprovalHandler;
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;

/**
 * 认证服务
 *
 * @author xiong.bo
 * @version 1.0
 * @date 2020/12/31 14:50
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;


    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("gold");
        return converter;
    }


    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                // 开启/oauth/token_key验证端口无权限访问
                .tokenKeyAccess("permitAll()")
                // 开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();
//        security.addTokenEndpointAuthenticationFilter(new CorsFilter(corsConfigurationSource()));
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("my-trusted-client")//客户端ID
                .secret(new BCryptPasswordEncoder().encode("secret"))//密码
                .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit", "client_credentials")
                .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
                .scopes("read", "write", "trust")//授权用户的操作权限
                .accessTokenValiditySeconds(60 * 60 * 3)//token有效期为3小时
                .refreshTokenValiditySeconds(60 * 60 * 3)//刷新token有效期为3小时
                .redirectUris("http://example.com");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
                .userApprovalHandler(userApprovalHandler)
                .authenticationManager(authenticationManager)
                // token生成方式
                .accessTokenConverter(accessTokenConverter())
                //接收GET和POST
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE);
    }
}

OAuth2SecurityConfiguration:

package com.generic.admin_scaffolding.config;

import com.generic.admin_scaffolding.service.impl.MyUserDetailsService;
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.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * security配置
 *
 * @author xiong.bo
 * @version 1.0
 * @date 2020/12/31 14:52
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private ClientDetailsService clientDetailsService;


    @Bean
    public MyUserDetailsService myUserDetailsService() {
        return new MyUserDetailsService();
    }

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


    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().and()
                .userDetailsService(myUserDetailsService())
                .passwordEncoder(new BCryptPasswordEncoder());
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
      /*http
      .anonymous().disable()
      .authorizeRequests()
      .antMatchers("/oauth/token").permitAll();*/
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        http.formLogin().permitAll();
        http.logout().permitAll().deleteCookies("JSESSIONID");
        http.csrf().disable();
        http.authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    @Bean
    @Autowired
    public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
        TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
        handler.setTokenStore(tokenStore);
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
        handler.setClientDetailsService(clientDetailsService);
        return handler;
    }

    @Bean
    @Autowired
    public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(tokenStore);
        return store;
    }
}

ResourceServerConfiguration:

package com.generic.admin_scaffolding.config;

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.provider.error.OAuth2AccessDeniedHandler;

/**
 * 资源服务
 *
 * @author xiong.bo
 * @version 1.0
 * @date 2020/12/31 14:52
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.
                exceptionHandling()
                .and()
                .csrf().disable() //打开的csrf保护
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//禁用session
                .and()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    }


}

3,AuthorizationServerConfiguration文件及其依赖文件

AuthorizationServerConfiguration类中,相关类都是直接import导入即可。

4,OAuth2SecurityConfiguration文件及其依赖文件

OAuth2SecurityConfiguration类中,相关类都是直接import导入即可,只有一个MyUserDetailsService类是需要再创建的。

MyUserDetailsService文件如下:

package com.generic.admin_scaffolding.service.impl;

import com.generic.admin_scaffolding.entity.model.SystemRole;
import com.generic.admin_scaffolding.entity.model.User;
import com.generic.admin_scaffolding.service.UserService;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

/**
 * @author xiong.bo
 * @version 1.0
 * @date 2020/12/31 15:41
 */
public class MyUserDetailsService implements UserDetailsService {

    @Resource
    private UserService userService;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByUsername(username);

        if(user == null){
            throw new UsernameNotFoundException("用户名:"+ username + "不存在!");
        }
        Collection<SimpleGrantedAuthority> collection = new HashSet<SimpleGrantedAuthority>();
        Iterator<SystemRole> iterator =  user.getRoleList().iterator();
        while (iterator.hasNext()){
            collection.add(new SimpleGrantedAuthority(iterator.next().getRoleCode()));
        }
        return new org.springframework.security.core.userdetails.User(username,user.getPassword(),collection);
    }
}

MyUserDetailsService类中涉及文件:UserService,User,SystemRole。

UserService类如下:

package com.generic.admin_scaffolding.service;

import com.generic.admin_scaffolding.common.Result;
import com.generic.admin_scaffolding.entity.dto.UserDto;
import com.generic.admin_scaffolding.entity.dto.UserRoleDTO;
import com.generic.admin_scaffolding.entity.model.User;

import java.util.List;

/**
 * @author xiong.bo
 * @version 1.0
 * @date 2020/11/18 17:09
 */
public interface UserService {


    /**
     * 用户列表
     *
     * @param page
     * @param pageSize
     * @return
     */
    Result<List<User>> findUserList(int page, int pageSize);

    //根据用户名查找用户实体
    User findByUsername(String username);

    /**
     * 新增用户
     *
     * @param user
     * @param userId 当前用户ID
     * @return
     */
    Result<User> saveUser(User user, Long userId);

    /**
     * 更新用户
     *
     * @param user
     * @param userId 当前用户ID
     * @return
     */
    Result<User> updateUser(User user, Long userId);

    /**
     * 用户修改密码
     *
     * @param userDto
     * @return
     */
    Result<User> updatePassword(UserDto userDto);

    /**
     * 删除用户
     *
     * @param id 主键id
     * @return
     */
    Result<Boolean> deleteUser(Long id);

    Result<User> findById(Long id);

    /**
     * 用户绑定角色
     *
     * @param roleIds
     * @return
     */
    Result bindingRole(UserRoleDTO roleIds);

    /**
     * 用户解除角色
     *
     * @param roleIds
     * @return
     */
    Result relieveRole(UserRoleDTO roleIds);
}

User类如下:

package com.generic.admin_scaffolding.entity.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * 用户信息表
 * 这里用户指登录系统的用户
 */

@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "t_user")
@Data
public class User extends BaseModel implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 用户名,登录名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 电话号码
     */
    private String phone;

    /**
     * 性别
     */
    private String sex;

    /**
     * 状态:1 启用,0 禁用
     */
    private Integer status;

    /**
     * 标识是否是管理员:0 默认普通用户,1 为系统管理员
     */
    @Column(name = "is_admin")
    private Integer isAdmin;

    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinTable(name = "t_user_role",
            joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
    private List<SystemRole> roleList = new ArrayList<SystemRole>();


    public List<SystemRole> getRoleList() {
        return roleList;
    }


}

SystemRole类如下:

package com.generic.admin_scaffolding.entity.model;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;

/**
 * 系统角色表
 */

@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "t_system_role")
@Data
public class SystemRole  extends BaseModel{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "role_code")
    private String roleCode;

    @Column(name = "role_name")
    private String roleName;

    private String remark;

    private Integer status;


}

5,ResourceServerConfiguration文件及其依赖文件

ResourceServerConfiguration类中,相关类都是直接import导入即可。

6,获取token

完成以上步骤的依赖配置,接下来测试验证。
获取token调用接口的步骤:

/**
 * 获取token调用接口
 * 步骤:
 * 1,localhost:8080/oauth/token
 * 2,Auth选择Basic Auth,
 *      username填configure配置的clientId,password填configure配置的secret
 * 3,body输入x-www-form-unlencoded格式,
 *      grant_type:password
 *      username:tom
 *      password:123456
 * 4,token是接口返回的access_token值
 * 5,token放到想要调用接口的Auth的Bearer Token中
 */

7,用户的密码加密

对登录用户的密码加密,使用security提供的BCryptPasswordEncoder类
//部分代码

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

userData.setPhone(user.getPhone());
userData.setSex(user.getSex());
userData.setPassword(passwordEncoder.encode(user.getPassword()));
/**
 * 推荐使用BCryptPasswordEncoder加密解密工具类
 * spring security中的BCryptPasswordEncoder方法采用SHA-256 +随机盐+密钥对密码进行加密。
 * SHA系列是Hash算法,不是加密算法,使用加密算法意味着可以解密(这个与编码/解码一样),但是采用Hash处理,其过程是不可逆的。
 * 加密(encode)
 * 密码匹配(matches)
 * BCrypt强哈希方法 每次加密的结果都不一样(即使同一个要加密的值)。
 *
 */

8,登录接口的核心代码

因为使用了这样的密码加密方式,对应的登录验证也要用下列这样的方式。

/**
 * 使用BCryptPasswordEncoder加密是不可逆的,
 * 因此通过用户名查找用户,再对比密码是否一致,
 * 这样就要求用户名是唯一的
 *
 * @param username 用户名
 * @param password 密码
 * @return
 */
@Override
public Result login(String username, String password) {
    if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
        throw new ServiceException(ExceptionDef.ERROR_COMMON_PARAM_NULL);
    }
    User user = userService.findByUsername(username);
    if (user == null) {
        throw new ServiceException(ExceptionDef.ERROR_USER_LOGIN_FAILED);
    }
    if (!passwordEncoder.matches(password, user.getPassword())) {
        throw new ServiceException(ExceptionDef.ERROR_USER_LOGIN_FAILED);
    }
    return Result.of(user);
}

9,结语

至此,权限模块的搭建的总体流程是这样,文章是通过代码的方式展示过程,文字简短,或许会有些看不懂,有建议和疑问欢迎评论,谢谢!
贴上本项目的git地址:https://github.com/beginner-john/admin_scaffolding.git

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot可以集成OAuth2和JWT实现安全的身份验证和授权。OAuth2是一种授权框架,允许用户授权第三方应用程序访问他们的资源。JWT是一种JSON Web Token,用于在客户端和服务器之间传递安全信息。 要集成OAuth2和JWT,需要使用Spring SecuritySpring Boot Starter Security。首先,需要配置OAuth2客户端和服务器端,以便客户端可以通过服务器端进行身份验证和授权。然后,需要配置JWT令牌,以便客户端可以使用令牌来访问受保护的资源。 在配置完成后,可以使用Spring Security的注释和过滤器来保护应用程序的资源。例如,可以使用@PreAuthorize注释来限制只有特定角色或权限的用户才能访问某些资源。 总之,Spring Boot集成OAuth2和JWT可以帮助开发人员实现安全的身份验证和授权,保护应用程序的资源免受未经授权的访问。 ### 回答2: SpringBoot是一种用于构建微服务的框架,它提供了许多便捷的功能,比如自动配置、内嵌容器等。OAuth2和JWT是现代Web应用程序中的两个关键技术,用于确保安全性和授权。本文将介绍如何使用SpringBoot集成OAuth2和JWTOAuth2是一个用于授权的开放标准,它让第三方可以利用某个授权服务器允许的授权进行访问。OAuth2有四种授权模式:授权码、简化模式、密码模式和客户端模式。其中,授权码模式和简化模式是最常用的。 为了实现集成OAuth2,我们需要以下几个步骤: 1. 添加Maven依赖。我们需要添加spring-security-oauth2和spring-security-jwt的依赖,这两个依赖用于构建我们的安全框架。 2. 创建一个安全配置类。我们可以使用@EnableAuthorizationServer和@EnableResourceServer注释来标记该类。这两个注释指示SpringBoot使用OAuth2和JWT作为授权和安全框架。 3. 配置OAuth2的凭据和权限。我们需要提供OAuth2服务器的凭据,这些凭据将用于授权客户端和资源服务器。此外,我们还需要配置每个客户端的权限。我们可以使用AuthorizationServerConfigurerAdapter类中的configure(ClientDetailsServiceConfigurer)方法来完成此操作。 4. 配置JWT的秘钥。JWT是一种基于JSON的开放标准,用于安全地传输信息。为了确保JWT的安全性,我们需要使用签名算法生成JWT的签名。我们可以使用AuthorizationServerConfigurerAdapter类中的tokenStore()方法和JwtAccessTokenConverter类来配置JWT。 以上就是SpringBoot集成OAuth2和JWT的基本步骤。集成后我们可以通过OAuth2获取访问令牌,并使用JWT对其进行签名和验证。这种集成为我们的应用程序带来了更高的安全性和授权功能,是现代Web应用程序中不可或缺的技术之一。 ### 回答3: OAuth2是目前公认的授权协议标准之一,它通过提供授权访问令牌的方式,实现了无需暴露用户凭证即可访问受保护的数据资源。 Spring Boot提供了许多集成Spring Security的方式,其中就包括对OAuth2的支持。Spring Boot OAuth2集成可帮助我们在应用程序中实现授权和认证的功能。 JWT是一种轻量级的认证和授权机制,在认证成功后,服务器会生成一个基于JSON的令牌,该令牌包含有关用户身份的信息,然后将其发送回客户端。客户端使用此令牌可以访问应用程序的资源。 Spring Boot同样支持对JWT的集成,以便在服务器端和客户端之间完成对称加密和解密的过程。 Spring Boot集成OAuth2和JWT通常需要实现以下步骤: 1. 添加依赖关系:为了使用OAuth2和JWT,我们需要在项目中添加相应的依赖关系。可以使用Maven或Gradle工具来添加依赖关系。 2. 配置OAuth2:在配置文件中添加OAuth2支持的客户端信息、访问令牌的有效期、授权服务器地址、安全配置和JWT密钥等信息。 3. 实现授权服务器:我们需要在应用程序中实现授权服务器,以便为已注册的客户端颁发令牌。这通常需要实现一些Spring Security过滤器,并添加一些额外的配置类来处理OAuth2请求和响应。 4. 注册用户服务:可以使用内存或数据库来存储用户信息,并使用Spring Security提供的相关类来注册用户服务。 5. 实现保护资源:添加Spring Security配置类以保护资源,访问这些资源需要提供有效的令牌。 6. 添加自定义JWT:如果需要自定义JWT,则需要添加Spring Security配置类,并实现相关的JWT生成和验证代码。 总之,Spring Boot的OAuth2和JWT支持让我们在应用程序中实现强大的授权和认证功能,保护我们的数据资源免受未经授权的访问。这些功能可以通过简单的配置和自定义实现来满足不同应用程序的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值