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