<?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.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.security</groupId>
<artifactId>spring_security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<mybatis.version>2.1.2</mybatis.version>
<druid.version>1.1.6</druid.version>
<jwt.version>1.0.9.RELEASE</jwt.version>
<jjwt.version>0.9.0</jjwt.version>
<fastJson.version>1.2.45</fastJson.version>
<mybatisPlus.version>3.3.1</mybatisPlus.version>
<common.version>3.5</common.version>
<swagger.version>2.8.0</swagger.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</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-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- 引入阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</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>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- JWT依赖 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!-- JSON工具 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastJson.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisPlus.version}</version>
</dependency>
<!-- StringUtilS工具 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${common.version}</version>
</dependency>
<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.security.common.enmun;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @Description: Response枚举
* @Author: LiuRunYong
* @Date: 2020/4/1
**/
@Getter
@AllArgsConstructor
public enum ResponseEnum {
/**
* 响应成功
*/
SUCCESS(200d, "成功"),
/**
* 响应失败
*/
ERROR(400d, "失败"),
/**
* 用户名密码错误
*/
USERNAME_PASSWORD_ERROR(401.1d, "用户名或密码错误"),
/**
* 用户冻结
*/
USER_LOCK(401.2d, "用户冻结"),
/**
* 无对应资源权限
*/
NO_ACCESS_PERMISSIONS(403d, "无对应资源权限"),
/**
* 系统异常
*/
EXCEPTION(500d, "系统异常"),
/**
* 未登录
*/
NOT_LOGIN(530d, "未登录");
private final Double code;
private final String desc;
}
package com.security.common.response;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.security.common.enmun.ResponseEnum;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletResponse;
import java.io.PrintWriter;
import java.io.Serializable;
/**
* @Description: 服务返回封装
* @Author: LiuRunYong
* * @Date: 2020/4/28
**/
// 保证序列化json的时候,如果对象为null,则不会转化为json
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Slf4j
public class ServerResponse<T> implements Serializable {
private Double status;
private String msg;
private String url;
private T data;
private ServerResponse(Double status) {
this.status = status;
}
private ServerResponse(Double status, T data) {
this.status = status;
this.data = data;
}
private ServerResponse(Double status, String msg, T data) {
this.data = data;
this.status = status;
this.msg = msg;
}
private ServerResponse(Double status, String msg) {
this.status = status;
this.msg = msg;
}
private ServerResponse(Double status, String msg, String url) {
this.status = status;
this.msg = msg;
this.url = url;
}
// 使该对象不在json序列化中
@JsonIgnore
public boolean isSuccess() {
return this.status.equals(ResponseEnum.SUCCESS.getCode());
}
public static <T> ServerResponse<T> createBySuccess() {
return new ServerResponse<T>(ResponseEnum.SUCCESS.getCode());
}
public static <T> ServerResponse<T> createBySuccessMessage(String msg) {
return new ServerResponse<T>(ResponseEnum.SUCCESS.getCode(), msg);
}
public static <T> ServerResponse<T> createBySuccess(T data) {
return new ServerResponse<T>(ResponseEnum.SUCCESS.getCode(), data);
}
public static <T> ServerResponse<T> createByERROR(T data) {
return new ServerResponse<T>(ResponseEnum.SUCCESS.getCode(), data);
}
public static <T> ServerResponse<T> createBySuccess(String msg, T data) {
return new ServerResponse<T>(ResponseEnum.SUCCESS.getCode(), msg, data);
}
public static <T> ServerResponse<T> createByError() {
return new ServerResponse<T>(ResponseEnum.ERROR.getCode(), ResponseEnum.ERROR.getDesc());
}
public static <T> ServerResponse<T> createByError(T data) {
return new ServerResponse<T>(ResponseEnum.ERROR.getCode(), data);
}
public static <T> ServerResponse<T> createByErrorMessage(String errorMessage) {
return new ServerResponse<T>(ResponseEnum.ERROR.getCode(), errorMessage);
}
public static <T> ServerResponse<T> createByCodeMessage(Double code, String errorMessage) {
return new ServerResponse<T>(code, errorMessage);
}
/**
* 无对应资源的权限
*
* @return ServerResponse
*/
public static ServerResponse noAccessPermissions() {
return new ServerResponse(ResponseEnum.NO_ACCESS_PERMISSIONS.getCode(), ResponseEnum.NO_ACCESS_PERMISSIONS.getDesc());
}
/**
* 未登录
*
* @return ServerResponse
*/
public static ServerResponse notLogin() {
return new ServerResponse(ResponseEnum.NOT_LOGIN.getCode(), ResponseEnum.NOT_LOGIN.getDesc());
}
/**
* 用户名或密码错误
*
* @return ServerResponse
*/
public static ServerResponse userNameOrPasswordError() {
return new ServerResponse(ResponseEnum.USERNAME_PASSWORD_ERROR.getCode(), ResponseEnum.USERNAME_PASSWORD_ERROR.getDesc());
}
/**
* 用户冻结
*
* @return ServerResponse
*/
public static ServerResponse userLock() {
return new ServerResponse(ResponseEnum.USER_LOCK.getCode(), ResponseEnum.USER_LOCK.getDesc());
}
/**
* 使用response输出JSON
*
* @param serverResponse 返回响应
*/
public static void createResponseEnumJson(ServletResponse response, ServerResponse serverResponse) {
PrintWriter out = null;
try {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
out = response.getWriter();
out.println(JSON.toJSONString(serverResponse));
} catch (Exception e) {
log.error("【JSON输出异常】" + e);
} finally {
if (out != null) {
out.flush();
out.close();
}
}
}
}
package com.security.common.handler;
import com.security.common.response.ServerResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description: 登录失败处理类
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Slf4j
@Component
public class UserLoginFailureHandler implements AuthenticationFailureHandler {
/**
* 登录失败返回结果
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
// 这些对于操作的处理类可以根据不同异常进行不同处理
if (exception instanceof UsernameNotFoundException) {
log.info("【登录失败】" + exception.getMessage());
ServerResponse.createResponseEnumJson(response, ServerResponse.userNameOrPasswordError());
}
if (exception instanceof LockedException) {
log.info("【登录失败】" + exception.getMessage());
ServerResponse.createResponseEnumJson(response, ServerResponse.userLock());
}
if (exception instanceof BadCredentialsException) {
log.info("【登录失败】" + exception.getMessage());
ServerResponse.createResponseEnumJson(response, ServerResponse.userNameOrPasswordError());
}
ServerResponse.createResponseEnumJson(response, ServerResponse.createByError());
}
}
package com.security.common.handler;
import com.security.common.jwt.JWTToken;
import com.security.common.response.ServerResponse;
import com.security.common.utils.ResultUtil;
import com.security.model.UserModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 登录成功处理类
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Slf4j
@Component
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler {
/**
* 登录成功返回结果
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// 组装JWT
UserModel userModel = (UserModel) authentication.getPrincipal();
ServerResponse.createResponseEnumJson(response, ServerResponse.createBySuccess(JWTToken.createAccessToken(userModel)));
}
}
package com.security.common.handler;
import com.security.common.response.ServerResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description: 用户登出类
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Component
public class UserLogoutSuccessHandler implements LogoutSuccessHandler {
/**
* 用户登出返回结果
* 这里应该让前端清除掉Token
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
SecurityContextHolder.clearContext();
ServerResponse.createResponseEnumJson(response, ServerResponse.createBySuccessMessage("登出成功"));
}
}
package com.security.common.handler;
import com.security.common.response.ServerResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description: 用户未登录处理类
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Component
public class UserNotLoginHandler implements AuthenticationEntryPoint {
/**
* 用户未登录返回结果
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
ServerResponse.createResponseEnumJson(response, ServerResponse.notLogin());
}
}
package com.security.common.handler;
import com.security.common.response.ServerResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description: 暂无权限处理类
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Component
public class UserNotPermissionHandler implements AccessDeniedHandler {
/**
* 暂无权限返回结果
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception) {
ServerResponse.createResponseEnumJson(response, ServerResponse.noAccessPermissions());
}
}
package com.security.common.jwt;
import com.alibaba.fastjson.JSON;
import com.security.model.UserModel;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
/**
* @Description: JWT工具类
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Slf4j
public class JWTToken {
/**
* 生成token
*
* @param userModel 自定义的用户对象
* @return String
*/
public static String createAccessToken(UserModel userModel) {
// 登陆成功生成JWT
String token = Jwts.builder()
// 放入用户名和用户ID
.setId(userModel.getUserId() + "")
// 主题
.setSubject(userModel.getAccount())
// 签发时间
.setIssuedAt(new Date())
// 签发者
.setIssuer("LiuRunYong")
// 自定义属性 放入用户拥有权限
.claim("authorities", JSON.toJSONString(userModel.getAuthorities()))
// 失效时间(一天)
.setExpiration(new Date(System.currentTimeMillis() + 24 * 3600000))
// 签名算法和密钥
.signWith(SignatureAlgorithm.HS512, "JWTSecret")
.compact();
return token;
}
}
package com.security.common.filter;
import com.alibaba.fastjson.JSONObject;
import com.security.model.UserModel;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
/**
* @Description: JWT接口请求校验拦截器请求接口时会进入这里验证Token是否合法和过期
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Slf4j
public class JWTAuthenticationTokenFilter extends BasicAuthenticationFilter {
public JWTAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, IOException {
// 获取请求头中JWT的Token
String tokenHeader = request.getHeader("Authorization");
if (null != tokenHeader) {
try {
// 解析JWT
Claims claims = Jwts.parser()
.setSigningKey("JWTSecret")
.parseClaimsJws(tokenHeader)
.getBody();
// 获取用户名
String username = claims.getSubject();
String userId = claims.getId();
if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(userId)) {
// 获取角色
Set<GrantedAuthority> authorities = new HashSet<>();
String authority = claims.get("authorities").toString();
if (!StringUtils.isEmpty(authority)) {
List<Map<String, String>> authorityMap = JSONObject.parseObject(authority, List.class);
authorityMap.forEach(
role -> authorities.add(new SimpleGrantedAuthority(role.get("authority")))
);
}
//组装参数
UserModel userModel = new UserModel();
userModel.setUserName(claims.getSubject()).setUserId(Integer.parseInt(claims.getId())).setAuthorities(authorities);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userModel, userId, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (ExpiredJwtException e) {
log.info("Token过期");
} catch (Exception e) {
log.info("Token无效");
}
}
filterChain.doFilter(request, response);
return;
}
}
package com.security.common.evaluator;
import com.security.model.PermissionModel;
import com.security.model.UserModel;
import com.security.service.UserService;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @Description: 自定义权限注解验证
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Component
public class UserPermissionEvaluator implements PermissionEvaluator {
@Resource
UserService userService;
public UserPermissionEvaluator() {
}
/**
* hasPermission鉴权方法
* 这里仅仅判断PreAuthorize注解中的permission
* 实际中可以根据业务需求设计数据库通过targetUrl和permission做更复杂鉴权
*
* @param authentication 用户身份
* @param targetUrl 请求路径
* @param permission 请求路径权限
* @return boolean 是否通过
*/
@Override
public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
// 获取用户信息
UserModel userModel = (UserModel) authentication.getPrincipal();
// 获取用户对应角色的权限(因为SQL中已经GROUP BY了,所以返回的list是不重复的)
List<PermissionModel> permissionModels = userService.selectUserModelByUserName(userModel.getUserName()).getPermissionModels();
List<String> rolePermissions = new ArrayList<>();
for (PermissionModel permissionModel : permissionModels) {
rolePermissions.add(permissionModel.getPermissionValue());
}
// 权限对比
return rolePermissions.contains(permission.toString());
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
return false;
}
}
package com.security.common.provider;
import com.security.model.RoleModel;
import com.security.model.UserModel;
import com.security.service.UserService;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @Description: 自定义登录验证
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Component
public class UserLoginProvider implements AuthenticationProvider {
final UserService userService;
public UserLoginProvider(UserService userService) {
this.userService = userService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 获取表单输入中返回的用户名
String userName = (String) authentication.getPrincipal();
// 获取表单中输入的密码
String password = (String) authentication.getCredentials();
// 查询用户是否存在
UserModel userModel = userService.selectUserModelByUserName(userName);
if (userModel == null) {
throw new UsernameNotFoundException("用户名不存在");
}
// 我们还要判断密码是否正确,这里我们的密码使用BCryptPasswordEncoder进行加密的
if (!new BCryptPasswordEncoder().matches(password, userModel.getPassword())) {
throw new BadCredentialsException("密码不正确");
}
// 还可以加一些其他信息的判断,比如用户账号已停用等判断
if (userModel.getState() != 0) {
throw new LockedException("该用户已被冻结");
}
// 角色集合
Set<GrantedAuthority> authorities = new HashSet<>();
// 查询用户角色
List<RoleModel> roleModels = userModel.getRoleModels();
// 循环添加角色信息
for (RoleModel roleModel : roleModels) authorities.add(new SimpleGrantedAuthority(roleModel.getRoleName()));
userModel.setAuthorities(authorities);
// 进行登录
return new UsernamePasswordAuthenticationToken(userModel, password, authorities);
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
package com.security.common.config;
import com.security.common.evaluator.UserPermissionEvaluator;
import com.security.common.filter.JWTAuthenticationTokenFilter;
import com.security.common.handler.*;
import com.security.common.provider.UserLoginProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.web.access.expression.DefaultWebSecurityExpressionHandler;
/**
* @Description:
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启权限注解,默认是关闭的
public class SecurityConfig extends WebSecurityConfigurerAdapter {
static String[] EXCLUDEPATH = {"/index", "/login/**", "/v2/api-docs", "/swagger-ui.html", "/swagger-resources/**", "/webjars/**"};
/**
* 自定义登录成功处理器
*/
final UserLoginSuccessHandler userLoginSuccessHandler;
/**
* 自定义登录失败处理器
*/
final UserLoginFailureHandler userLoginFailureHandler;
/**
* 自定义注销成功处理器
*/
final UserLogoutSuccessHandler userLogoutSuccessHandler;
/**
* 自定义暂无权限处理器
*/
final UserNotPermissionHandler userNotPermissionHandler;
/**
* 自定义未登录的处理器
*/
final UserNotLoginHandler userNotLoginHandler;
/**
* 自定义登录逻辑验证器
*/
final UserLoginProvider userLoginProvider;
public SecurityConfig(UserLoginSuccessHandler userLoginSuccessHandler,
UserLoginFailureHandler userLoginFailureHandler,
UserLogoutSuccessHandler userLogoutSuccessHandler,
UserNotPermissionHandler userNotPermissionHandler,
UserNotLoginHandler userNotLoginHandler,
UserLoginProvider userLoginProvider) {
this.userLoginSuccessHandler = userLoginSuccessHandler;
this.userLoginFailureHandler = userLoginFailureHandler;
this.userLogoutSuccessHandler = userLogoutSuccessHandler;
this.userNotPermissionHandler = userNotPermissionHandler;
this.userNotLoginHandler = userNotLoginHandler;
this.userLoginProvider = userLoginProvider;
}
/**
* 加密方式
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 注入自定义PermissionEvaluator
*/
@Bean
public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
handler.setPermissionEvaluator(new UserPermissionEvaluator());
return handler;
}
/**
* 配置登录验证逻辑
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) {
// 这里可启用我们自己的登陆验证逻辑
auth.authenticationProvider(userLoginProvider);
}
/**
* 配置security的控制逻辑
*
* @param http 请求
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 从配置文件获取不用进行权限验证的请求或资源
.antMatchers(EXCLUDEPATH).permitAll()
// 其他的需要登陆后才能访问
.anyRequest().authenticated()
.and()
// 配置未登录自定义处理类
.httpBasic().authenticationEntryPoint(userNotLoginHandler)
.and()
// 配置登录地址
.formLogin()
.loginProcessingUrl("/login/userLogin")
// 配置登录成功自定义处理类
.successHandler(userLoginSuccessHandler)
// 配置登录失败自定义处理类
.failureHandler(userLoginFailureHandler)
.and()
// 配置退出地址
.logout()
.logoutUrl("/login/userLogout")
// 配置用户登出自定义处理类
.logoutSuccessHandler(userLogoutSuccessHandler)
.and()
// 配置没有权限自定义处理类
.exceptionHandling().accessDeniedHandler(userNotPermissionHandler)
.and()
// 开启跨域
.cors()
.and()
// 取消跨站请求伪造防护
.csrf().disable();
// 基于Token不需要session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 禁用缓存
http.headers().cacheControl();
// 添加JWT过滤器
http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager()));
}
}
package com.security.model;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @Description: PermissionModel
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@ApiModel(description = "权限对象Bean")
public class PermissionModel implements Serializable {
@ApiModelProperty(value = "用户主键(新增?,更新*)")
private Integer permissionId;
@ApiModelProperty(value = "权限名称")
private String permissionName;
@ApiModelProperty(value = "权限值")
private String permissionValue;
@ApiModelProperty(value = "权限类型(0:目录,1:菜单,2:按钮)")
private String permissionType;
@ApiModelProperty(value = "权限状态(0:可用,1:不可用)")
private String permissionState;
@ApiModelProperty(value = "上级编号")
private String superiorId;
@ApiModelProperty(value = "创建时间")
private String createTime;
@ApiModelProperty(value = "更新时间")
private String updateTime;
}
package com.security.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
/**
* @Description: RoleModel
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@ApiModel(description = "角色对象Bean")
public class RoleModel implements Serializable {
@ApiModelProperty(value = "主键(新增?,更新*)")
private Integer roleId;
@ApiModelProperty(value = "角色名称")
@TableField()
private String roleName;
@ApiModelProperty(value = "角色标题")
private String roleTitle;
@ApiModelProperty(value = "状态(0:可用,1:不可用)")
private String state;
@ApiModelProperty(value = "描述", hidden = true)
private String description;
@ApiModelProperty(value = "创建时间")
private String createTime;
@ApiModelProperty(value = "更新时间", hidden = true)
private String updateTime;
}
package com.security.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
/**
* @Description: UserModel
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@ApiModel(description = "用户Bean")
@ToString(exclude = "password")
public class UserModel implements Serializable {
@ApiModelProperty(value = "主键(新增?,更新*)")
private Integer userId;
@ApiModelProperty(value = "账号", hidden = true)
private String account;
@ApiModelProperty(value = "用户名称")
private String userName;
@ApiModelProperty(value = "密码", hidden = true)
@JsonIgnore
private String password;
@ApiModelProperty(value = "手机号")
private String phone;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "性别(0:男,1:女)")
private Integer sex;
@ApiModelProperty(value = "用户身份证号")
private Integer idCard;
@ApiModelProperty(value = "用户状态(0:可用,1:不可用,2:暂时锁定)")
private Integer state;
@ApiModelProperty(value = "创建时间", hidden = true)
private String createTime;
@ApiModelProperty(value = "修改时间", hidden = true)
private String updateTime;
@ApiModelProperty(value = "角色", hidden = true)
private Set<GrantedAuthority> authorities;
@ApiModelProperty(value = "用户角色", hidden = true)
private List<RoleModel> roleModels;
@ApiModelProperty(value = "用户权限", hidden = true)
private List<PermissionModel> permissionModels;
}
package com.security.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.security.model.UserModel;
/**
* @Description: UserMapper
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
public interface UserMapper extends BaseMapper<UserModel> {
/**
* 根据用户名查询用户
*
* @param userName 用户名称
* @return UserModel
*/
UserModel selectUserModelByUserName(String userName);
}
package com.security.service;
import com.security.model.UserModel;
/**
* @Description:
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
public interface UserService {
/**
* 根据用户名称查询用户信息
*
* @param userName 用户名称
* @return UserModel
*/
UserModel selectUserModelByUserName(String userName);
}
package com.security.impl;
import com.security.dao.UserMapper;
import com.security.model.PermissionModel;
import com.security.model.RoleModel;
import com.security.model.UserModel;
import com.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @Description:
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
final UserMapper userMapper;
public UserServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public UserModel selectUserModelByUserName(String userName) {
return userMapper.selectUserModelByUserName(userName);
}
}
package com.security.controller;
import com.security.model.UserModel;
import com.security.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description:
* @Author: LiuRunYong
* @Date: 2020/4/28
**/
@RestController
@RequestMapping("/user/")
@Api(tags = "用户模块")
public class UserController {
final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping(value = "list")
@PreAuthorize("hasPermission(null ,'system_manage')")
@ApiOperation(value = "用户列表")
public UserModel userList() {
return userService.selectUserModelByUserName("187123456789");
}
}
package com.security;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.security.dao")
public class SpringSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityApplication.class, args);
}
}
package com.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @Description: Swagger2配置
* @Author: LiuRunYong
* @Date: 2020/4/29
**/
@Configuration
@EnableSwagger2
public class Swagger2 {
/**
* swagger2的配置文件
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.security.controller"))
.paths(PathSelectors.any())
.build();
}
/**
* 构建 api文档的详细信息函数,注意这里的注解引用的是哪个
*/
private ApiInfo apiInfo() {
// 获取工程名称
String projectName = System.getProperty("user.dir");
return new ApiInfoBuilder()
.title(projectName.substring(projectName.lastIndexOf("\\") + 1) + " API接口文档")
.contact(new Contact("Liurunyong", "http://www.baidu.com", "*********@qq.com"))
.version("1.0")
.description("API文档")
.build();
}
}
# 服务端口
server:
port: 10713
# 解密工具
jasypt:
encryptor:
password: EWRREWRERWECCCXC
algorithm: PBEWithMD5AndDES
# 日志输出
logging:
level:
com.security.dao: debug
# 数据库相关配置
spring:
application:
name: cloud-provider-payment
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:10224/security?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: ENC(u1XJHLCnjmjlBvIpF7mA1g==)
password: ENC(qtBFnHfnGN8ew58eFcm6bDvmz45bKPsO)
下来项目除了**“EXCLUDEPATH”、请求头中不要“Authorization"** 其他的请求必须添加(这是重点)