权限类说明:
权限配置类:ShiroConfiguration (权限核心配置类)、AuthFilter(拦截器配置类)、AuthRealm(认证类)、AuthTokenVo、NoPermissionException(无权限异常处理类)、JWTUtil(token工具类)、AuthConstant(提示类)、ReturnMessage 返回信息类
测试需要的类:User UserController UserList UserPermission UserRole
maven坐标
权限核心配置
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.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @ClassName ShiroConfiguration
* @Description
* @Author XinChunYu
* @Date 2020/5/29 13:42
* @Version 1.0
**/
@Configuration
public class ShiroConfiguration {
private org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ShiroConfiguration.class);
//从配置文件里面读取是否需要启动登录认证的开关,默认true
//@Value("${jwt.auth}")
private boolean auth = true;
//配置拦截器
@Bean
public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
//启用认证
String openAuth = auth ? "auth" : "anon";
//自定义过滤器链
Map<String, javax.servlet.Filter> filters = new HashMap<>();
//指定拦截器处理
filters.put("auth", new com.lzqs.yuanzilian.shiro.AuthFilter());
shiroFilterFactoryBean.setFilters(filters);
Map<String, String> filterMap = new LinkedHashMap<>();
//登录请求不拦截
filterMap.put("/user/login", "anon");
//登录页面需要用到的接口,不拦截
filterMap.put("/user/fetchCurrentUser", "anon");
//拦截所有接口请求,做权限判断
filterMap.put("/**", openAuth);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
logger.info("Shiro拦截器工厂类注入成功");
return shiroFilterFactoryBean;
}
// SecurityManager 安全管理器;Shiro的核心
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm());
return securityManager;
}
//自定义身份认证realm
@Bean
public AuthRealm userRealm() {
return new AuthRealm();
}
@Bean("lifecycleBeanPostProcessor")
//管理shiro生命周期
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
//Shiro注解支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager((org.apache.shiro.mgt.SecurityManager) securityManager);
return authorizationAttributeSourceAdvisor;
}
}
权限过滤器
package com.lzqs.yuanzilian.shiro;
import com.alibaba.fastjson.JSONObject;
import com.lzqs.yuanzilian.constant.ReturnMessage;
import com.lzqs.yuanzilian.shiro.util.AuthConstant;
import com.lzqs.yuanzilian.shiro.util.JWTUtil;
import io.micrometer.core.instrument.util.StringUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName AuthFilter
* @Description 实现自定义的认证拦截器,接收传过来的token,实现前后端分离的权限认证
* @Author XinChunYu
* @Date 2020/5/29 14:03
* @Version 1.0
**/
public class AuthFilter extends AuthenticatingFilter {
private org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AuthorizingRealm.class);
private ReturnMessage responseResult = ReturnMessage.failWithMsg(AuthConstant.AUTHENTICATE_FAIL);
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
return null;
}
/**
* 在这里拦截所有请求
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
String token = JWTUtil.getRequestToken((HttpServletRequest)request);
if (!StringUtils.isBlank(token)){
try {
this.executeLogin(request, response);
} catch (Exception e) {
// 应用异常
logger.info(e.getMessage());
responseResult = ReturnMessage.failWithMsg(e.getMessage());
return false;
}
} else {
// cookie中未检查到token或token为空
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
String httpMethod = httpServletRequest.getMethod();
String requestURI = httpServletRequest.getRequestURI();
responseResult = ReturnMessage.failWithMsg(AuthConstant.TOKEN_BLANK);
logger.info("请求 {} 的Token为空 请求类型 {}", requestURI, httpMethod);
return false;
}
return true;
}
/**
* 请求失败拦截,请求终止,不进行转发直接返回客户端拦截结果
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception{
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
httpServletResponse.setContentType("application/json; charset=utf-8");
httpServletResponse.setCharacterEncoding("UTF-8");
//ReturnMessage returnMessage = ReturnMessage.failWithMsg(AuthConstant.AUTHENTICATE_FAIL);
httpServletResponse.getWriter().print(JSONObject.toJSONString(responseResult));
return false;
}
/**
* 用户存在,执行登录认证
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
String token = JWTUtil.getRequestToken((HttpServletRequest)request);
AuthTokenVo jwtToken = new AuthTokenVo(token);
// 提交给AuthRealm进行登录认证
getSubject(request, response).login(jwtToken);
return true;
}
}
token认证和权限认证
import com.lzqs.yuanzilian.shiro.controller.User;
import com.lzqs.yuanzilian.shiro.controller.UserList;
import com.lzqs.yuanzilian.shiro.controller.UserPermission;
import com.lzqs.yuanzilian.shiro.controller.UserRole;
import com.lzqs.yuanzilian.shiro.util.AuthConstant;
import io.micrometer.core.instrument.util.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* @ClassName AuthRealm
* @Description
* @Author XinChunYu
* @Date 2020/5/29 13:51
* @Version 1.0
**/
public class AuthRealm extends AuthorizingRealm {
private org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AuthorizingRealm.class);
/**
* 重写,绕过身份令牌异常导致的shiro报错
* @param authenticationToken
* @return
*/
@Override
public boolean supports(AuthenticationToken authenticationToken){
return authenticationToken instanceof AuthTokenVo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
logger.info("用户角色权限认证");
//获取用户登录信息
User user = (User)principals.getPrimaryPrincipal();
//添加角色和权限
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
for(UserRole userRole : user.getUserRoleList()){
authorizationInfo.addRole(userRole.getUserRoleName());
for(UserPermission permissionVo : userRole.getUserPermissionList()){
authorizationInfo.addStringPermission(permissionVo.getUserPermissionName());
}
}
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
logger.info("执行认证逻辑");
//获得token
String token = (String)authenticationToken.getCredentials();
//获得token中的用户信息
String username = JwtAuthenticator.getUsername(token);
//判空
if(StringUtils.isBlank(username)){
throw new AuthenticationException(AuthConstant.TOKEN_BLANK);
}
User user = null;
try{
for(User u : UserList.userList){
if(u.getUsername().equals(username)){
user = u;
}
}
//查询用户是否存在
if(user == null){
throw new AuthenticationException(AuthConstant.TOKEN_INVALID);
//token过期
}else if(!(JwtAuthenticator.verifyToken(token, username, user.getPassword()))){
throw new AuthenticationException(AuthConstant.TOKEN_EXPIRE);
}
}catch (Exception e){
throw e;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user,
token,
getName());
return authenticationInfo;
}
}
jwt token对象
import org.apache.shiro.authc.AuthenticationToken;
/**
* @ClassName AuthTokenVo
* @Description
* @Author XinChunYu
* @Date 2020/5/29 14:09
* @Version 1.0
**/
public class AuthTokenVo implements AuthenticationToken {
private String token;
public AuthTokenVo(String token){
this.token = token;
}
@Override
public Object getPrincipal(){
return token;
}
@Override
public Object getCredentials(){
return token;
}
}
异常处理
import com.alibaba.fastjson.JSONObject;
import com.lzqs.yuanzilian.constant.ReturnMessage;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName NoPermissionException
* @Description
* @Author XinChunYu
* @Date 2020/5/30 15:12
* @Version 1.0
**/
@ControllerAdvice
public class NoPermissionException {
/**
* 用户角色权限认证 当没有权限时会报UnauthorizedException异常 此处处理异常给前端返回提示语
* @param response
* @param ex
* @throws IOException
*/
@ResponseBody
@ExceptionHandler(UnauthorizedException.class)
public void handleShiroException(HttpServletResponse response, Exception ex) throws IOException {
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
httpServletResponse.setContentType("application/json; charset=utf-8");
httpServletResponse.setCharacterEncoding("UTF-8");
//ReturnMessage returnMessage = ReturnMessage.failWithMsg(AuthConstant.AUTHENTICATE_FAIL);
httpServletResponse.getWriter().print(JSONObject.toJSONString(ReturnMessage.failWithMsg("访问了无权限目录")));
}
//执行认证逻辑认证不通过时会走 过滤器AuthFilter中的onAccessDenied方法 所以这个异常不捕获
/* @ResponseBody
@ExceptionHandler(AuthorizationException.class)
public String AuthorizationException(Exception ex) {
return "权限认证失败";
}*/
}
jwt token工具类
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.Date;
/**
* @ClassName JWTUtil
* @Description
* @Author XinChunYu
* @Date 2020/5/29 13:18
* @Version 1.0
**/
public class JWTUtil {
// 过期时间50分钟
private static final long EXPIRE_TIME = 50*60*1000;
/**
* 校验token是否正确
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String username, String secret) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 获得token中的信息无需secret解密也能获得
* @return token中包含的用户名
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成签名,5min后过期
* @param username 用户名
* @param secret 用户的密码
* @return 加密的token
*/
public static String sign(String username, String secret) {
try {
Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date)
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
return null;
}
}
public static String getRequestToken(HttpServletRequest request) {
return request.getHeader("token");
}
}
认证提示
package com.lzqs.yuanzilian.shiro.util;
/**
* @ClassName AuthConstant
* @Description 权限相关的常量
* @Author XinChunYu
* @Date 2020/5/29 14:06
* @Version 1.0
**/
public class AuthConstant {
/**
* cookie中存储的token字段名
*/
public final static String COOKIE_TOKEN_NAME = "Authorization";
/**
* token有效时间 时*分*秒*1000L
*/
public final static Long EXPIRE_TIME = 3*60*1000L;//先设置3分钟
//登录认证结果,返回给前端
public final static String UNKNOWN_ACCOUNT = "登录失败, 用户不存在。";
public final static String WRONG_PASSWORD = "登录失败,密码错误。";
public final static String TOKEN_BLANK = "验证失败,token为空,请登录。";
public final static String TOKEN_INVALID = "验证失败,token错误。";
public final static String TOKEN_EXPIRE = "验证失败,token过期,请重新登录。";
public final static String AUTHENTICATE_FAIL = "无访问权限,请尝试登录或联系管理员。";
}
ReturnMessage返回提示类
package com.lzqs.yuanzilian.constant;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 通用的返回类
**/
public class ReturnMessage implements Serializable {
//状态码 100-成功,200-失败 400-带信息失败
private int code;
//提示信息
private String msg;
//用户要返回给浏览器的数据
private Map<String, Object> data = new LinkedHashMap<>();
public static ReturnMessage successWithData(Map<String, Object> data) {
ReturnMessage result = new ReturnMessage();
result.setCode(100);
result.setMsg("成功");
result.setData(data);
return result;
}
public static ReturnMessage success() {
ReturnMessage result = new ReturnMessage();
result.setCode(100);
result.setMsg("成功");
return result;
}
public static ReturnMessage successWithMsg(String newmsg){
ReturnMessage result = new ReturnMessage();
result.setCode(100);
result.setMsg(newmsg);
return result;
}
public static ReturnMessage fail() {
ReturnMessage result = new ReturnMessage();
result.setCode(200);
result.setMsg("系统错误");
return result;
}
public static ReturnMessage failWithMsg(String newmsg) {
ReturnMessage result = new ReturnMessage();
result.setCode(400);
result.setMsg(newmsg);
return result;
}
/**
* 失败 + 自定义msg,缺省为"处理失败"
*
* @param newmsg
* @return
*/
public static ReturnMessage failWithMsg(int code, String newmsg) {
ReturnMessage result = new ReturnMessage();
result.setCode(code);
if (StringUtils.isEmpty(newmsg)) {
newmsg = "处理失败";
}
result.setMsg(newmsg);
return result;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
public ReturnMessage add(String key, Object value) {
this.getData().put(key, value);
return this;
}
}
controller控制器
import com.lzqs.yuanzilian.constant.ReturnMessage;
import com.lzqs.yuanzilian.shiro.util.AuthConstant;
import com.lzqs.yuanzilian.shiro.util.JWTUtil;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Map;
/**
* @ClassName UserController
* @Description
* @Author XinChunYu
* @Date 2020/5/29 14:15
* @Version 1.0
**/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
@ResponseBody
public ReturnMessage login(@RequestBody Map<String, Object> map){
String username = map.get("username").toString();
String password = map.get("password").toString();
User user = null;
for(User u : UserList.userList){
if(u.getUsername().equals(username)){
user = u;
}
}
if(user == null){
//账号不存在
return ReturnMessage.failWithMsg(AuthConstant.UNKNOWN_ACCOUNT);
}else if(!user.getPassword().equals(password)){
//密码错误
return ReturnMessage.failWithMsg(AuthConstant.WRONG_PASSWORD);
}else{
//通过认证, 生成签名
String token = JWTUtil.sign(user.getUsername(), user.getPassword());
return ReturnMessage.success().add("token", token);
}
}
@RequiresPermissions("shop")
@RequestMapping("/shop")
@ResponseBody
public ReturnMessage shop(@RequestBody Map<String, Object> map){
return ReturnMessage.success();
}
@RequiresPermissions("order")
@RequestMapping("/order")
@ResponseBody
public ReturnMessage order(@RequestBody Map<String, Object> map){
return ReturnMessage.success();
}
/**
* 不加@RequiresPermissions 注解时不会进行用户授权认证
* @param map
* @return
*/
@RequestMapping("/game")
@ResponseBody
public ReturnMessage game(@RequestBody Map<String, Object> map){
return ReturnMessage.success();
}
@RequestMapping("/address")
@ResponseBody
public ReturnMessage address(@RequestBody Map<String, Object> map){
return ReturnMessage.success();
}
}
用户类
package com.lzqs.yuanzilian.shiro.controller;
import java.util.List;
/**
* @ClassName UserBean
* @Description 用户表
* @Author XinChunYu
* @Date 2020/5/29 13:16
* @Version 1.0
**/
public class User {
private Long userId;
private String username;
private String password;
private String salt;//盐值
private List<UserRole> userRoleList;
public User() {
}
public User(Long userId, String username, String password, String salt, List<UserRole> userRoleList) {
this.userId = userId;
this.username = username;
this.password = password;
this.salt = salt;
this.userRoleList = userRoleList;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<UserRole> getUserRoleList() {
return userRoleList;
}
public void setUserRoleList(List<UserRole> userRoleList) {
this.userRoleList = userRoleList;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
}
角色类
package com.lzqs.yuanzilian.shiro.controller;
import java.util.List;
/**
* @ClassName UserRole
* @Description 角色表
* @Author XinChunYu
* @Date 2020/5/29 13:34
* @Version 1.0
**/
public class UserRole {
private Long userRoleId;
private String userRoleName;
List<UserPermission> userPermissionList;
public UserRole(Long userRoleId, String userRoleName, List userPermissionList) {
this.userRoleId = userRoleId;
this.userRoleName = userRoleName;
this.userPermissionList = userPermissionList;
}
public Long getUserRoleId() {
return userRoleId;
}
public void setUserRoleId(Long userRoleId) {
this.userRoleId = userRoleId;
}
public String getUserRoleName() {
return userRoleName;
}
public void setUserRoleName(String userRoleName) {
this.userRoleName = userRoleName;
}
public List<UserPermission> getUserPermissionList() {
return userPermissionList;
}
public void setUserPermissionList(List<UserPermission> userPermissionList) {
this.userPermissionList = userPermissionList;
}
}
权限
package com.lzqs.yuanzilian.shiro.controller;
/**
* @ClassName Permission
* @Description 权限表
* @Author XinChunYu
* @Date 2020/5/29 13:35
* @Version 1.0
**/
public class UserPermission {
private Long userPermissionId;
private String userPermissionName;
public UserPermission(Long userPermissionId, String userPermissionName) {
this.userPermissionId = userPermissionId;
this.userPermissionName = userPermissionName;
}
public Long getUserPermissionId() {
return userPermissionId;
}
public void setUserPermissionId(Long userPermissionId) {
this.userPermissionId = userPermissionId;
}
public String getUserPermissionName() {
return userPermissionName;
}
public void setUserPermissionName(String userPermissionName) {
this.userPermissionName = userPermissionName;
}
}
用户集合在这就不建表查数据库了
package com.lzqs.yuanzilian.shiro.controller;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName UserList
* @Description
* @Author XinChunYu
* @Date 2020/5/29 16:15
* @Version 1.0
**/
public class UserList {
public static List<User> userList = new ArrayList<>();
static{
ArrayList<UserPermission> userPermissionArrayList1 = new ArrayList<>();
userPermissionArrayList1.add(new UserPermission(1l,"shop"));
userPermissionArrayList1.add(new UserPermission(2l,"game"));
userPermissionArrayList1.add(new UserPermission(1l,"order"));
userPermissionArrayList1.add(new UserPermission(1l,"address"));
ArrayList<UserRole> userRoleArrayList1 = new ArrayList<>();
userRoleArrayList1.add(new UserRole(1L, "老板", userPermissionArrayList1));
User user1 = new User(1l, "admin", "123456", "iwejfiwjf", userRoleArrayList1);
ArrayList<UserPermission> userPermissionArrayList2 = new ArrayList<>();
userPermissionArrayList2.add(new UserPermission(1l,"shop"));
userPermissionArrayList2.add(new UserPermission(2l,"game"));
ArrayList<UserRole> userRoleArrayList2 = new ArrayList<>();
userRoleArrayList2.add(new UserRole(2L, "经理", userPermissionArrayList2));
User user2 = new User(2l, "xiaoming", "123456", "jfiosjfos", userRoleArrayList2);
userList.add(user1);
userList.add(user2);
}
}
maven坐标
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.0</version>
</dependency>