赞
踩
1.简洁(Compact):可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快。
2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库。
3.安全(security): 与简单的JSON相比,XML和XML数字签名会引入复杂的安全漏洞。
1.用户登陆之后,使用密码对账号进行签名生成并返回token并设置过期时间;
2.将token保存到本地,并且每次发送请求时都在header上携带token。
3.shiro过滤器拦截到请求并获取header中的token,并提交到自定义realm的doGetAuthenticationInfo方法。
4.通过jwt解码获取token中的用户名,从数据库中查询到密码之后根据密码生成jwt效验器并对token进行验证。
通俗点来说,就是服务器根据给定的密钥和算法,对用户名或者ID之类(只要是能判断是某一个用户的唯一标识都行)加上过期时间的时间戳进行加密,然后生成类似XXX.XXX.XXX的字符串,这个字符串就是所谓的token,以后要想访问服务器得到资源,只需要在请求头带上token,服务器拿到这个token,再进行解密验证操作,判断该用户是否是有效用户,然后放行。
OK!介绍完这些我们直接上代码,一些关键代码我已加注释进行说明。这里我由于个人习惯集成了Swagger,如果不需要可自行忽略Swagger部分。
程序说明:使用不同的用户身份进行验证,只有特定的用户才能访问特定的资源。
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`username` varchar(25) NOT NULL,
`password` varchar(25) NOT NULL,
`role` varchar(25) DEFAULT NULL,
`permission` varchar(25) DEFAULT NULL,
`ban` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `role` (
`id` int(11) NOT NULL,
`role` varchar(25) DEFAULT NULL,
`permission` varchar(25) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
<?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 http://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>1.5.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.chen</groupId>
<artifactId>permissiontest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>permissiontest</name>
<description>Springboot、Shiro、JWT实现权限管理</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>2.1.0</version>
</dependency>
<!--mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--整合Shiro安全框架-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--集成jwt实现token认证-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.2.0</version>
</dependency>
<!-- 集成swagger接口文档 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<!-- swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring.datasource.url = jdbc:mysql://localhost:3306/test?characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.username = root
spring.datasource.password = 123456
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
mybatis.mapperLocations = classpath:mapper/**/*.xml
#log日志输出位置
logging.file=log/log.log
#打印数据库crud详情
logging.level.com.chen.mapper=DEBUG
# 控制台日志输出级别
logging.level.org.springframework.web=DEBUG
package com.chen.config;
import com.chen.filter.JWTFilter;
import com.chen.shiro.CustomRealm;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description Shiro配置类
*/
@Configuration
public class ShiroConfig {
/**
* 先经过token过滤器,如果检测到请求头存在 token,则用 token 去 login,接着走 Realm 去验证
*/
@Bean
public ShiroFilterFactoryBean factory(SecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new LinkedHashMap<>();
//设置我们自定义的JWT过滤器
filterMap.put("jwt", new JWTFilter());
factoryBean.setFilters(filterMap);
factoryBean.setSecurityManager(securityManager);
// 设置无权限时跳转的 url;
factoryBean.setUnauthorizedUrl("/unauthorized/无权限");
Map<String, String> filterRuleMap = new HashMap<>();
// 所有请求通过我们自己的JWT Filter
filterRuleMap.put("/**", "jwt");
// 放行不需要权限认证的接口
//放行Swagger接口
filterRuleMap.put("/v2/api-docs","anon");
filterRuleMap.put("/swagger-resources/configuration/ui","anon");
filterRuleMap.put("/swagger-resources","anon");
filterRuleMap.put("/swagger-resources/configuration/security","anon");
filterRuleMap.put("/swagger-ui.html","anon");
filterRuleMap.put("/webjars/**","anon");
//放行登录接口和其他不需要权限的接口
filterRuleMap.put("/login", "anon");
filterRuleMap.put("/unauthorized/**", "anon");
factoryBean.setFilterChainDefinitionMap(filterRuleMap);
return factoryBean;
}
/**
* 注入 securityManager
*/
@Bean
public SecurityManager securityManager(CustomRealm customRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置自定义 realm.
securityManager.setRealm(customRealm);
/*
* 关闭shiro自带的session
*/
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
/**
* 添加注解支持
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// 强制使用cglib,防止重复代理和可能引起代理出错的问题
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
}
package com.chen.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
/**
* @author chen
* @date 2019/7/24
* @email 15218979950@163.com
* @description Swagger2配置类
*/
@Configuration
@EnableSwagger2
public class Swagger2Config extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.chen.controller"))
.paths(PathSelectors.any())
.build();
}
//构建 api文档的详细信息函数,注意这里的注解引用的是哪个
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
//页面标题
.title("SpringBoot集成Shiro、JWT实现鉴权Demo")
//创建人
.contact(new Contact("Eric 陈", "", ""))
//版本号
.version("1.0")
//描述
.description("使用SpringBoot整合Shiro、集成JWT对用户权限进行管理")
.build();
}
private List<ApiKey> security() {
return newArrayList(
new ApiKey("token", "token", "header")
);
}
}
package com.chen.filter;
import com.chen.shiro.JWTToken;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description 自定义过滤器,对token进行处理
*/
public class JWTFilter extends BasicHttpAuthenticationFilter {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 如果带有 token,则对 token 进行检查,否则直接通过
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
//判断请求的请求头是否带上 "token"
if (isLoginAttempt(request, response)) {
//如果存在,则进入 executeLogin 方法执行登入,检查 token 是否正确
try {
executeLogin(request, response);
return true;
} catch (Exception e) {
//token 错误
responseError(response, e.getMessage());
}
}
//如果请求头不存在 token,则可能是执行登陆操作或者是游客状态访问,无需检查 token,直接返回 true
return true;
}
/**
* 判断用户是否想要登入。
* 检测 header 里面是否包含 token 字段
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String token = req.getHeader("token");
return token != null;
}
/**
* 执行登陆操作
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader("token");
JWTToken jwtToken = new JWTToken(token);
// 提交给realm进行登入,如果错误它会抛出异常并被捕获
getSubject(request, response).login(jwtToken);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
/**
* 将非法请求跳转到 /unauthorized/**
*/
private void responseError(ServletResponse response, String message) {
try {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
//设置编码,否则中文字符在重定向时会变为空字符串
message = URLEncoder.encode(message, "UTF-8");
httpServletResponse.sendRedirect("/unauthorized/" + message);
} catch (IOException e) {
logger.error(e.getMessage());
}
}
}
package com.chen.shiro;
import com.chen.service.UserService;
import com.chen.util.JWTUtil;
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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description 自定义Realm,实现Shiro安全认证
*/
@Component
public class CustomRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 必须重写此方法,不然会报错
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JWTToken;
}
/**
* 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("————身份认证方法————");
String token = (String) authenticationToken.getCredentials();
// 解密获得username,用于和数据库进行对比
String username = JWTUtil.getUsername(token);
if (username == null || !JWTUtil.verify(token, username)) {
throw new AuthenticationException("token认证失败!");
}
/* 以下数据库查询可根据实际情况,可以不必再次查询,这里我两次查询会很耗资源
* 我这里增加两次查询是因为考虑到数据库管理员可能自行更改数据库中的用户信息
*/
String password = userService.getPassword(username);
if (password == null) {
throw new AuthenticationException("该用户不存在!");
}
int ban = userService.checkUserBanStatus(username);
if (ban == 1) {
throw new AuthenticationException("该用户已被封号!");
}
return new SimpleAuthenticationInfo(token, token, "MyRealm");
}
/**
* 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("————权限认证————");
String username = JWTUtil.getUsername(principals.toString());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获得该用户角色
String role = userService.getRole(username);
//每个角色拥有默认的权限
String rolePermission = userService.getRolePermission(username);
//每个用户可以设置新的权限
String permission = userService.getPermission(username);
Set<String> roleSet = new HashSet<>();
Set<String> permissionSet = new HashSet<>();
//需要将 role, permission 封装到 Set 作为 info.setRoles(), info.setStringPermissions() 的参数
roleSet.add(role);
permissionSet.add(rolePermission);
permissionSet.add(permission);
//设置该用户拥有的角色和权限
info.setRoles(roleSet);
info.setStringPermissions(permissionSet);
return info;
}
}
package com.chen.shiro;
import org.apache.shiro.authc.AuthenticationToken;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description 对token进行扩展
*/
public class JWTToken implements AuthenticationToken {
private String token;
public JWTToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}
package com.chen.util;
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 java.io.UnsupportedEncodingException;
import java.util.Date;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description JWT工具类
*/
public class JWTUtil {
// 过期时间 24 小时
private static final long EXPIRE_TIME = 60 * 24 * 60 * 1000;
// 密钥
private static final String SECRET = "chen";
/**
* 生成 token
*/
public static String createToken(String username) {
try {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(SECRET);
// 附带username信息
return JWT.create()
.withClaim("username", username)
//到期时间
.withExpiresAt(date)
//创建一个新的JWT,并使用给定的算法进行标记
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
return null;
}
}
/**
* 校验 token 是否正确
*/
public static boolean verify(String token, String username) {
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
//在token中附带了username信息
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
//验证 token
verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 获得token中的信息,无需secret解密也能获得
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
}
(这是我自己封装的一个信息返回工具类,一般来说这东西大同小异,根据自己的需要自己封装即可)
package com.chen.model;
import java.util.HashMap;
import java.util.Map;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description 用于返回前端json数据的工具类
*/
public class Msg {
// 状态码
private int status;
// 提示信息
private String message;
// 封装有效数据
private Map<String, Object> data = new HashMap<String, Object>();
public static Msg success() {
Msg result = new Msg();
result.setStatus(200);
result.setMessage("success");
return result;
}
public static Msg fail() {
Msg result = new Msg();
result.setStatus(400);
result.setMessage("fail");
return result;
}
public static Msg noPermission() {
Msg result = new Msg();
result.setStatus(401);
result.setMessage("no permission");
return result;
}
public static Msg error() {
Msg result = new Msg();
result.setStatus(500);
result.setMessage("error");
return result;
}
public static Msg code(int code){
Msg result = new Msg();
result.setStatus(code);
result.setMessage("exception");
return result;
}
public Msg add(String key, Object value) {
this.data.put(key, value);
return this;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
}
package com.chen.mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description mapper接口
*/
@Repository
public interface UserMapper {
/**
* 获得密码
*/
String getPassword(String username);
/**
* 获得角色权限
*/
String getRole(String username);
/**
* 修改密码
*/
void updatePassword(@Param("username") String username, @Param("newPassword") String newPassword);
/**
* 获得存在的用户
*/
List<String> getUser();
/**
* 封号
*/
void banUser(String username);
/**
* 检查用户状态
*/
int checkUserBanStatus(String username);
/**
* 获得用户角色默认的权限
*/
String getRolePermission(String username);
/**
* 获得用户的权限
*/
String getPermission(String username);
}
package com.chen.service;
import com.chen.mapper.UserMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description
*/
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public String getPassword(String username){
return userMapper.getPassword(username);
}
public int checkUserBanStatus(String username){
return userMapper.checkUserBanStatus(username);
}
public String getRole(String username){
return userMapper.getRole(username);
}
public String getRolePermission(String username){
return userMapper.getRolePermission(username);
}
public String getPermission(String username){
return userMapper.getPermission(username);
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chen.mapper.UserMapper">
<select id="getPassword" parameterType="String" resultType="String">
SELECT password
FROM user
WHERE username = #{username}
</select>
<select id="getRole" parameterType="String" resultType="String">
SELECT role
FROM user
WHERE username = #{username}
</select>
<update id="updatePassword" parameterType="String">
UPDATE user
SET password = #{password}
WHERE username = #{username}
</update>
<select id="getUser" resultType="String">
SELECT username
FROM user
</select>
<update id="banUser" parameterType="String">
UPDATE user
SET ban = 1
WHERE username = #{username}
</update>
<select id="checkUserBanStatus" parameterType="String" resultType="int">
SELECT ban
FROM user
WHERE username = #{username}
</select>
<select id="getRolePermission" parameterType="String" resultType="String">
SELECT role.permission
FROM user, role
WHERE username = #{username} AND role.role = user.role
</select>
<select id="getPermission" parameterType="String" resultType="String">
SELECT permission
FROM user
WHERE username = #{username}
</select>
</mapper>
package com.chen.controller;
import com.chen.model.Msg;
import org.apache.shiro.ShiroException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description 对异常进行返回处理
*/
@RestControllerAdvice
public class ExceptionController {
// 捕捉shiro的异常
@ExceptionHandler(ShiroException.class)
public Msg handle401() {
return Msg.noPermission().add("info","您没有权限访问!");
}
// 捕捉其他所有异常
@ExceptionHandler(Exception.class)
public Msg globalException(HttpServletRequest request, Throwable ex) {
return Msg.code(getStatus(request).value()).add("info","访问出错,无法访问: " + ex.getMessage());
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}
}
package com.chen.controller;
import com.chen.model.Msg;
import com.chen.service.UserService;
import com.chen.util.JWTUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
/**
* @author chen
* @date 2019/7/23
* @email 15218979950@163.com
* @description
*/
@RestController
@Api(value = "测试Controller")
public class UserController {
@Autowired
private UserService userService;
@ApiOperation(value = "用户登录", notes = "登录--不进行拦截")
@PostMapping("/login")
public Msg login(@RequestParam("username") String username,
@RequestParam("password") String password) {
String realPassword = userService.getPassword(username);
if (realPassword == null) {
return Msg.fail().add("info","用户名错误");
} else if (!realPassword.equals(password)) {
return Msg.fail().add("info","密码错误");
} else {
return Msg.success().add("token",JWTUtil.createToken(username));
}
}
@ApiOperation(value = "无权限", notes = "无权限跳转的接口")
@RequestMapping(path = "/unauthorized/{message}")
public Msg unauthorized(@PathVariable String message) throws UnsupportedEncodingException {
return Msg.fail().add("info",message);
}
@ApiOperation(value = "特定用户访问", notes = "拥有 user, admin 角色的用户可以访问下面的页面")
@PostMapping("/getMessage")
@RequiresRoles(logical = Logical.OR, value = {"user", "admin"})
public Msg getMessage() {
return Msg.success().add("info","成功获得信息!");
}
@ApiOperation(value = "Vip用户访问", notes = "拥有 vip 权限可以访问该页面")
@PostMapping("/getVipMessage")
@RequiresRoles(logical = Logical.OR, value = {"user", "admin"})
@RequiresPermissions("vip")
public Msg getVipMessage() {
return Msg.success().add("info","成功获得 vip 信息!");
}
}
package com.chen;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication
@MapperScan(value = "com.chen.mapper")
@EnableSwagger2
public class PermissiontestApplication {
public static void main(String[] args) {
SpringApplication.run(PermissiontestApplication.class, args);
}
}
建议:步骤总的来说比较多,不过基本很容易理解,核心部分还是在Shiro配置和JWT配置那部分,只要搞清楚认证和鉴权的逻辑顺序,基本就能掌握。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。