验证流程
登录 -> security 登录验证 -> 验证通过,返回Token给前端 , Token包含登录用户所拥有的所有权限
前端每次请求接口时,携带Token -> security 拦截器解析Token,并判断有没有权限访问这个接口
用户角色权限表设计
用户表
CREATE TABLE `admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_email` varchar(64) DEFAULT NULL COMMENT '用户邮箱',
`password` varchar(50) DEFAULT NULL COMMENT '密码',
`user_name` varchar(20) DEFAULT NULL COMMENT '用户名',
`mobile` varchar(20) DEFAULT NULL COMMENT '手机',
`department` varchar(64) DEFAULT NULL COMMENT '部门',
`browser` varchar(64) DEFAULT NULL COMMENT '使用浏览器',
`address` varchar(250) DEFAULT NULL COMMENT '地址',
`updater` int(11) DEFAULT NULL COMMENT '更改人',
`last_login` datetime DEFAULT NULL COMMENT '最后登录时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`pwd` varchar(11) DEFAULT NULL,
`create_user` int(11) DEFAULT NULL COMMENT '添加人',
`status` int(11) NOT NULL DEFAULT '1' COMMENT '状态 1:使用 2:禁止',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
用户角色关系表
CREATE TABLE `admin_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`admin_id` int(11) NOT NULL COMMENT '管理员id',
`role_id` int(11) NOT NULL COMMENT '权限id',
`ctime` datetime DEFAULT NULL COMMENT '创建时间',
`dtime` datetime DEFAULT NULL COMMENT '修改时间',
`cuser` int(11) DEFAULT NULL COMMENT '创建人',
`duser` int(11) DEFAULT NULL COMMENT '修改人',
`status` int(1) DEFAULT NULL COMMENT '有没有效(1:有效;0:无效)',
PRIMARY KEY (`id`),
KEY `ADMIN_ROLE_ADMIN_ID` (`admin_id`) USING BTREE,
KEY `ADMIN_ROLE_ROLE_ID` (`role_id`),
CONSTRAINT `FK_ADMIN_ROLE_ROLE_ID` FOREIGN KEY (`role_id`) REFERENCES `role` (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;
权限表
CREATE TABLE `permission` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`parent_id` bigint(20) DEFAULT NULL COMMENT '父权限',
`name` varchar(64) DEFAULT NULL COMMENT '权限名称',
`enname` varchar(64) NOT NULL COMMENT '权限英文名称',
`url` varchar(100) DEFAULT NULL COMMENT '授权路径',
`type` char(1) NOT NULL COMMENT '类目:0菜单1操作',
`description` longtext COMMENT '备注',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_status` char(1) DEFAULT '0' COMMENT '逻辑删除:0保留1删除',
PRIMARY KEY (`ID`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='菜单权限表';
权限角色关系表
CREATE TABLE `role_permission` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) DEFAULT NULL COMMENT '角色id',
`permission_id` int(11) DEFAULT NULL COMMENT '权限id',
`ctime` datetime DEFAULT NULL COMMENT '创建时间',
`dtime` datetime DEFAULT NULL COMMENT '更新时间',
`cuser` int(11) DEFAULT NULL COMMENT '创建者id',
`duser` int(11) DEFAULT NULL COMMENT '更新者id',
`status` tinyint(3) DEFAULT NULL COMMENT '有没有效',
PRIMARY KEY (`ID`) USING BTREE,
KEY `FK_Reference_27` (`role_id`) USING BTREE,
KEY `FK_Reference_28` (`permission_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=733 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='角色和权限的对应表';
SpringSecurity 实战
目录结构:
JWTAuthenticationTokenFilter
import com.alibaba.fastjson.JSONObject;
import com.wuyuan.cs.manage.component.security.utils.JWTTokenUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
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 org.springframework.util.StringUtils;
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.ArrayList;
import java.util.List;
import java.util.Map;
/**
* JWT接口请求校验拦截器
* 请求接口时会进入这里验证Token是否合法和过期,如果令牌合法则获取用户信息,并且存入SecurityContextHolder
*/
@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 {
// 获取请求头中JWT的Token
String tokenHeader = request.getHeader(JWTTokenUtil.HEAD_KEY);
if (null != tokenHeader && tokenHeader.startsWith(JWTTokenUtil.TOKEN_PREFIX)) {
try {
// 截取JWT前缀
String token = tokenHeader.replace(JWTTokenUtil.TOKEN_PREFIX, "");
// 解析JWT
Claims claims = JWTTokenUtil.parseJWT(token);
// 获取用户名
String username = claims.getSubject();
String password = claims.getId();
if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
// 获取权限
List<GrantedAuthority> authorities = new ArrayList<>();
String authority = claims.get("authorities").toString();
System.out.println(authority);
if (!StringUtils.isEmpty(authority)) {
List<Map<String, String>> authorityMap = JSONObject.parseObject(authority, List.class);
for (Map<String, String> auth : authorityMap) {
if (null != auth) {
authorities.add(new SimpleGrantedAuthority(auth.get("authority")));
}
}
}
// 从token中拿到登录的用户信息和权限 放到security的上下文中
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (ExpiredJwtException e) {
log.error("Token过期");
} catch (Exception e) {
log.error("Token无效");
}
}
filterChain.doFilter(request, response);
}
}
UserAccessDecisionManager
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.Iterator;
/**
* @Description: 访问决策管理器, 请求url权限验证
*/
@Component
@Slf4j
public class UserAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
Iterator<ConfigAttribute> iterator = collection.iterator();
while (iterator.hasNext()) {
ConfigAttribute ca = iterator.next();
//当前请求需要的权限
String needRole = ca.getAttribute();
//当前用户所具有的权限
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(needRole)) {
return;
}
}
}
throw new AccessDeniedException("权限不足!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
UserAccessDeniedHandler
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.common.utils.WebError;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@Slf4j
public class UserAccessDeniedHandler implements AccessDeniedHandler {
//权限拒绝处理逻辑
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
SifarResult result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_INSUFFICIENT_PERMISSION,"权限不足");
httpServletResponse.setContentType("text/json;charset=utf-8");
log.error("权限不足!");
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
UserAuthenticationEntryPoint
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.common.utils.WebError;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Description: 匿名用户访问无权限资源时的异常
*/
@Component
@Slf4j
public class UserAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
SifarResult result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_HAS_NOT_LOGIN,"用户未登录");
log.error("用户未登录!");
httpServletResponse.setContentType("text/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
UserAuthenticationFailureHandler
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.common.utils.WebError;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Description: 登录失败处理逻辑
*/
@Component
public class UserAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
//返回json数据
SifarResult result = null;
if (e instanceof AccountExpiredException) {
//账号过期
result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"session过期");
} else if (e instanceof BadCredentialsException) {
//密码错误
result = SifarResult.error(WebError.ERROR_ADMIN_NAME_OR_PASSWORD,"账号或者用户名错误");
} else if (e instanceof CredentialsExpiredException) {
//密码过期
result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"密码过期");
} else if (e instanceof DisabledException) {
//账号不可用
result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"账号不可用");
} else if (e instanceof LockedException) {
//账号锁定
result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"账号锁定");
} else if (e instanceof InternalAuthenticationServiceException) {
//用户不存在
result = SifarResult.error(WebError.ERROR_USER_DOSE_NOT_EXIST,"用户不存在");
}else{
//其他错误
result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"其他错误");
}
//处理编码方式,防止中文乱码的情况
httpServletResponse.setContentType("text/json;charset=utf-8");
//塞到HttpServletResponse中返回给前台
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
UserAuthenticationSuccessHandler
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.dto.LoginDTO;
import com.wuyuan.cs.manage.common.pojo.Admin;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.component.security.utils.JWTTokenUtil;
import com.wuyuan.cs.manage.service.AdminService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
/**
* @Description: 登录成功处理逻辑
*/
@Component
@Slf4j
public class UserAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private AdminService adminService;
@SneakyThrows
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication){
log.info("登录成功");
// 将用户权限生成token
// User userDetails = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User userDetails = (User) authentication.getPrincipal();
String token = JWTTokenUtil.createAccessToken(userDetails);
token = JWTTokenUtil.TOKEN_PREFIX + token;
// 将用户信息和权限菜单和token返回给前端
Admin admin = adminService.getAdminByName(userDetails.getUsername());
SifarResult<LoginDTO> login = adminService.login(admin);
LoginDTO loginDTO = login.getContent();
loginDTO.setToken(token);
//处理编码方式,防止中文乱码的情况
httpServletResponse.setContentType("application/json;charset=utf-8");
//返回json数据
SifarResult result = SifarResult.success(loginDTO);
PrintWriter out = httpServletResponse.getWriter();
out.write(JSON.toJSONString(result));
out.flush();
out.close();
}
}
UserFilterInvocationSecurity
import com.wuyuan.cs.manage.common.pojo.PermissionNew;
import com.wuyuan.cs.manage.service.PermissionNewService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import java.util.Collection;
import java.util.List;
/**
* @Description:
*/
@Component
public class UserFilterInvocationSecurity implements FilterInvocationSecurityMetadataSource {
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Value("${server.servlet.context-path}")
private String contextPath;
@Autowired
private PermissionNewService permissionNewService;
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
//获取请求地址
String requestUrl = ((FilterInvocation) o).getHttpRequest().getRequestURI();
if(requestUrl.contains("/actuator")){
return null;
}
if (requestUrl.contains(contextPath)) {
requestUrl = requestUrl.replace(contextPath, "");
}
List<PermissionNew> permissionList = permissionNewService.getPermissionNewListByUrl(requestUrl);
if(permissionList == null || permissionList.size() == 0){
//请求路径没有配置权限,表明该请求接口可以任意访问
return null;
}
String[] attributes = new String[permissionList.size()];
for(int i = 0;i<permissionList.size();i++){
attributes[i] = permissionList.get(i).getEnname();
}
return SecurityConfig.createList(attributes);
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
UserLogoutSuccessHandler
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Description: 登出成功处理逻辑
*/
@Component
@Slf4j
public class UserLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
SifarResult result = SifarResult.success("退出登录成功");
log.info("退出登录成功");
httpServletResponse.setContentType("text/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
UserSessionInformationExpiredStrategy
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.common.utils.WebError;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Description: 会话信息过期策略
*/
@Component
@Slf4j
public class UserSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent sessionInformationExpiredEvent) throws IOException, ServletException {
SifarResult result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"session过期");
log.error("session过期");
HttpServletResponse httpServletResponse = sessionInformationExpiredEvent.getResponse();
httpServletResponse.setContentType("text/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(result));
}
}
JWTTokenUtil
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.TimeUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import java.text.ParseException;
import java.util.Date;
@Slf4j
public class JWTTokenUtil {
/**
* JWT的过期时间
*/
// // 过期时间是3600秒,既是1个小时
// private static final long EXPIRATION = 3600L;
//
// // 选择了记住我之后的过期时间为7天
// private static final long EXPIRATION_REMEMBER = 604800L;
/**
* 过期时间,毫秒,一天
*/
public static final long JWT_EXPIRE = 60 * 60 * 24*1000;
/**
* JWT的生成密钥
*/
public static final String JWT_SECURITY = "JWTSecret";
/**
* 请求的 Header KEY
*/
public static final String HEAD_KEY = "Authorization";
/**
* 生成的Token的前缀:
*/
public static final String TOKEN_PREFIX = "YuChen-";//"Bearer "
/**
* 生成Token
*/
public static String createAccessToken(UserDetails userDetails) throws ParseException {
// long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION;
String auth = JSON.toJSONString(userDetails.getAuthorities());
System.out.println("========" +userDetails.getPassword());
// 登陆成功生成JWT
String token = Jwts.builder()
//设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
.setId(userDetails.getUsername())
// 主题(代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。)
.setSubject(userDetails.getUsername())
// 签发时间
.setIssuedAt(TimeUtil.GetGreenDate())
// 签发者
.setIssuer("YuChen")
// 自定义属性 放入用户拥有权限
.claim("authorities", auth)
// 失效时间
.setExpiration(new Date(TimeUtil.GetGreenDate().getTime() + JWT_EXPIRE))
// 设置签名使用的签名算法和签名使用的秘钥
//生成签名的时候使用的秘钥secret,这个方法在本地封装,一般可以从本地配置文件中读取,切记这个秘钥不能外露哦。
// 它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
.signWith(SignatureAlgorithm.HS512, JWT_SECURITY)
//就开始压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
.compact();
return token;
}
/**
* @Description 解析JWT-Token
*/
public static Claims parseJWT(String jwt) throws Exception {
//得到DefaultJwtParser
Claims claims = Jwts.parser()
//设置签名的秘钥
.setSigningKey(JWT_SECURITY)
//设置需要解析的jwt
.parseClaimsJws(jwt).getBody();
return claims;
}
/**
* 获取token的过期时间
*
* @param token token
* @return 过期时间
*/
public Date getExpirationDateFromToken(String token) throws Exception {
return parseJWT(token)
.getExpiration();
}
/**
* 判断token是否过期
*
* @param token token
* @return 已过期返回true,未过期返回false
*/
private Boolean isTokenExpired(String token) throws Exception {
Date expiration = getExpirationDateFromToken(token);
return expiration.before(TimeUtil.GetGreenDate());
}
}
PasswordEncoderMD5
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class PasswordEncoderMD5 implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
//rawPassword:客户端获取的密码 encodedPassword:数据库查询出的密码
String rawPasswordMD5 = UserManager.md5Pswd(rawPassword.toString());
return rawPasswordMD5.equals(encodedPassword);
}
}
UserManager
import cn.hutool.crypto.digest.DigestUtil;
import com.wuyuan.cs.manage.common.pojo.Admin;
public class UserManager {
/**
* 加工密码,和登录一致。
* @param user
* @return
*/
public static Admin md5Pswd(Admin user){
//密码为 email + '#' + pswd,然后MD5
user.setPassword(md5Pswd(user.getUserName(),user.getPassword()));
return user;
}
/**
* 字符串返回值
* @param /email
* @param pswd
* @return
*/
public static String md5Pswd(String name ,String pswd){
pswd = String.format("%s#%s", name,pswd);
pswd = DigestUtil.md5Hex(pswd);
return pswd;
}
public static String md5Pswd(String pswd){
pswd = DigestUtil.md5Hex(pswd);
return pswd;
}
public static void main(String[] args) {
String pwd = md5Pswd("123456");
System.out.println(pwd);
}
}
UserDetailsServiceImpl
import cn.hutool.core.util.StrUtil;
import com.wuyuan.cs.manage.common.pojo.Admin;
import com.wuyuan.cs.manage.common.pojo.PermissionNew;
import com.wuyuan.cs.manage.common.pojo.Role;
import com.wuyuan.cs.manage.service.AdminService;
import com.wuyuan.cs.manage.service.PermissionNewService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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 java.util.ArrayList;
import java.util.List;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private AdminService adminService;
@Autowired
private PermissionNewService permissionNewService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Admin admin = adminService.getAdminByName(username);
if(null == admin){
throw new UsernameNotFoundException("用户不存在!");
}
// 查询用户拥有的角色集合
List<Role> roleList = adminService.getRoleListById(admin.getId());
// 查询用户拥有的权限集合
List<PermissionNew> permissionList = permissionNewService.getPermissionNewByUid(admin.getId());
// 用户权限=【角色+权限】
List<GrantedAuthority> authorities = new ArrayList<>();
for (Role rolemap : roleList) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_" + rolemap.getRoleName());
authorities.add(grantedAuthority);
}
for (PermissionNew permissionmap : permissionList) {
if (!StrUtil.isEmpty(permissionmap.getEnname())) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permissionmap.getEnname());
authorities.add(grantedAuthority);
}
}
return new User(admin.getUserName(), admin.getPassword(), authorities);
}
}
WebSecurityConfig
import com.wuyuan.cs.manage.component.security.handler.*;
import com.wuyuan.cs.manage.component.security.utils.PasswordEncoderMD5;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
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.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import javax.sql.DataSource;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//登录成功处理逻辑
@Autowired
UserAuthenticationSuccessHandler authenticationSuccessHandler;
//登录失败处理逻辑
@Autowired
UserAuthenticationFailureHandler authenticationFailureHandler;
//权限拒绝处理逻辑
@Autowired
UserAccessDeniedHandler accessDeniedHandler;
//匿名用户访问无权限资源时的异常
@Autowired
UserAuthenticationEntryPoint authenticationEntryPoint;
//会话失效(账号被挤下线)处理逻辑
// @Autowired
// UserSessionInformationExpiredStrategy sessionInformationExpiredStrategy;
//登出成功处理逻辑
@Autowired
UserLogoutSuccessHandler logoutSuccessHandler;
//访问决策管理器
@Autowired
UserAccessDecisionManager accessDecisionManager;
//实现权限拦截
@Autowired
UserFilterInvocationSecurity securityMetadataSource;
@Autowired
UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource; // 数据源
@Override
@Bean
public UserDetailsService userDetailsService() {
//获取用户账号密码及权限信息
return new UserDetailsServiceImpl();
}
@Bean
public PasswordEncoderMD5 passwordEncoder() {
return new PasswordEncoderMD5();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(new PasswordEncoderMD5());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 开启跨域 ,取消跨站请求伪造防护
http.cors().and().csrf().disable();
http.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.antMatchers("/shell/**").permitAll()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(accessDecisionManager);//决策管理器
o.setSecurityMetadataSource(securityMetadataSource);//安全元数据源
return o;
}
}).
//登出
and().logout().
permitAll().//允许所有用户
logoutSuccessHandler(logoutSuccessHandler).//登出成功处理逻辑
deleteCookies("JSESSIONID").//登出之后删除cookie
//登入
and().formLogin().
permitAll().//允许所有用户
successHandler(authenticationSuccessHandler).//登录成功处理逻辑
failureHandler(authenticationFailureHandler)//登录失败处理逻辑
.and().rememberMe()
.userDetailsService(userDetailsService) // 设置userDetailsService
// .tokenRepository(persistentTokenRepository()) // 设置数据访问层
.tokenValiditySeconds(60*60*24). // 记住我的时间(秒)
//异常处理(权限拒绝、登录失效等)
and().exceptionHandling().
accessDeniedHandler(accessDeniedHandler).//权限拒绝处理逻辑
authenticationEntryPoint(authenticationEntryPoint);//匿名用户访问无权限资源时的异常处理
//会话管理
// and().sessionManagement().
// maximumSessions(1).//同一账号同时登录最大用户数
// expiredSessionStrategy(sessionInformationExpiredStrategy);//会话失效(账号被挤下线)处理逻辑
// http.addFilterBefore(securityInterceptor, JWTAuthenticationTokenFilter.class);
// 基于Token不需要session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 禁用缓存
http.headers().cacheControl();
// 添加JWT过滤器
http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager()));
}
/**
* 持久化token
*
* Security中,默认是使用PersistentTokenRepository的子类InMemoryTokenRepositoryImpl,将token放在内存中
* 如果使用JdbcTokenRepositoryImpl,会创建表persistent_logins,将token持久化到数据库
*/
/* @Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource); // 设置数据源
// tokenRepository.setCreateTableOnStartup(true); // 启动创建表,创建成功后注释掉
return tokenRepository;
}*/
}