今天的主要任务是将权限和安全管理搭建完成,首先是在pom中引入相关的jar包
然后在指定的位置创建文件
ShiroConfig是指定shiro注入关系的配置类,这里我直接贴出代码
package com.mansh.vschool.config;
import com.mansh.vschool.custom.DatabaseRealm;
import com.mansh.vschool.custom.JWTFilter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean("lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
@Bean
public DatabaseRealm getDatabaseRealm(){
DatabaseRealm myShiroRealm = new DatabaseRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(getDatabaseRealm());
return securityManager;
}
@Bean
protected ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/login", "noSessionCreation,anon");
chainDefinition.addPathDefinition("/**", "noSessionCreation,JWTToken");
return chainDefinition;
}
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
filterFactory.setSecurityManager(securityManager);
Map<String, Filter> filterMap = filterFactory.getFilters();
filterMap.put("JWTToken",new JWTFilter());
filterFactory.setFilters(filterMap);
filterFactory.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());
return filterFactory;
}
@Bean
public SessionStorageEvaluator sessionStorageEvaluator(){
DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
sessionStorageEvaluator.setSessionStorageEnabled(false);
return sessionStorageEvaluator;
}
}
JWTUtils是工具类,封装了对JWT的创建,读取,检验各种操作
package com.mansh.vschool.utils;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
@Slf4j
public class JWTUtils {
@Autowired
private RedisUtils redisUtils;
public String createToken() throws UnsupportedEncodingException {
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
Date now = new Date();
String token = JWT.create()
.withHeader(map)
.withIssuer("mansh")
.withSubject("vschool")
.withIssuedAt(now)
.withExpiresAt(expireDate(now,0,0,0,0,10,0))
.sign(Algorithm.HMAC256("HMS256"));
return token;
}
public String createTokenWithClaim(Map<String,String> claims) throws UnsupportedEncodingException {
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
Date now = new Date();
JWTCreator.Builder builder = JWT.create();
for(Map.Entry entry:claims.entrySet()){
builder.withClaim((String)entry.getKey(),(String)entry.getValue());
}
builder.withHeader(map)
.withIssuer("mansh")
.withSubject("vschool")
.withIssuedAt(now)
.withExpiresAt(expireDate(now,0,0,0,0,10,0));
String secret = IdUtil.simpleUUID();
String token = builder.sign(Algorithm.HMAC256(secret));
redisUtils.set(token+"-secret",secret);
return token;
}
/**
* 验证token的签名 防止伪造
* @param token
* @return
*/
public DecodedJWT verify(String token){
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256((String)redisUtils.get(token+"-secret")))
.withIssuer("mansh")
.build();
jwt = verifier.verify(token);
} catch (TokenExpiredException e) {
e.printStackTrace();
log.error(e.getMessage());
throw e;
} catch (Exception e){
e.printStackTrace();
log.error(e.getMessage());
}
return jwt;
}
public String get(String token,String name){
DecodedJWT decodedJWT = JWT.decode(token);
String re = "";
Map claims = decodedJWT.getClaims();
if(claims.containsKey(name)){
re = decodedJWT.getClaim(name).asString();
}
return re;
}
public Date expireDate(Date now,int year,int month,int day,int hour,int minute,int second){
if(now == null){
now = new Date();
}
if(year != 0){
now = DateUtil.offset(now, DateField.YEAR,year);
}
if(month != 0){
now = DateUtil.offset(now, DateField.MONTH,month);
}
if(day != 0){
now = DateUtil.offset(now, DateField.DAY_OF_MONTH,day);
}
if(hour != 0){
now = DateUtil.offset(now, DateField.HOUR_OF_DAY,hour);
}
if(minute != 0){
now = DateUtil.offset(now, DateField.MINUTE,minute);
}
if(second != 0){
now = DateUtil.offset(now, DateField.SECOND,second);
}
return now;
}
}
JWTFilter是一个Filter,主要任务是每次请求过来的时候进行检验的
package com.mansh.vschool.custom;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.mansh.vschool.user.service.IMshUserService;
import com.mansh.vschool.utils.JWTUtils;
import com.mansh.vschool.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@Slf4j
public class JWTFilter extends FormAuthenticationFilter {
@Autowired
private JWTUtils jwtUtils;
@Autowired
private RedisUtils redisUtils;
@Autowired
private IMshUserService userService;
@Autowired
private ParamConst paramConst;
@Override
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue){
HttpServletRequest req = (HttpServletRequest)request;
String URI = req.getRequestURI();
String token = req.getHeader("authToken");
try {
DecodedJWT decodedJwt = jwtUtils.verify(token);
if(decodedJwt == null){
//token被篡改
return false;
}else{
//token正常,检测是否超时
if(checkTimeOut(token)){
//已超时
return false;
}else{
//未超时,验证角色权限
return checkAuth(token,URI);
}
}
} catch (TokenExpiredException e) {
//token失效 但是还在刷新时间内的情况
if(checkTimeOut(token)){
//已超时
return false;
}else{
//未超时,验证角色权限,重发token
//TODO 重发逻辑待定,需要前端配合修改
return checkAuth(token,URI);
}
}
}
public boolean checkTimeOut(String token){
long lastReflushDate = (long)redisUtils.get(token+"-reflushDate");
return System.currentTimeMillis() - lastReflushDate > Long.parseLong(paramConst.getTokenTimeOut());
}
public boolean checkAuth(String token,String URI){
List<String> privList = userService.getPrivListByName(jwtUtils.get(token,"userName"));
for(String priv:privList){
if(URI.equals(priv)){
//有权限 更新访问时间 通过验证
redisUtils.del(token+"-reflushDate");
redisUtils.set(token+"-reflushDate",System.currentTimeMillis());
return true;
}
}
return false;
}
}
我用一张图简单描述一下我的思路,做一下记录
今天仅仅完成到这个程度,以后再使用的过程中如果发现bug在进行调整吧