参考:
https://www.inlighting.org/archives/spring-boot-shiro-jwt.html
https://www.doufuplus.com/blog/shiro-jwt03.html
项目目录
pom文件如下
<?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>
<groupId>com.xyy</groupId>
<artifactId>mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/>
</parent>
<properties>
<!-- <thymeleaf-version>3.0.0.RELEASE</thymeleaf-version>
<thymeleaf-layout-dialect.version>2.0.0</thymeleaf-layout-dialect.version>-->
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
</dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-annotation -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
<version>3.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
表数据:
Response类,为返回统一格式的数据
package com.xyy.util;
public class Response {
// http 状态码
private int code;
// 返回信息
private String msg;
// 返回的数据
private Object data;
public Response(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
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 Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
User
package com.xyy.pojo;
import lombok.Data;
public class User {
private Long id;
private String username;
private String password;
private String role;
private String permission;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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 String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", role='" + role + '\'' +
", permission='" + permission + '\'' +
'}';
}
}
UserService
package com.xyy.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xyy.pojo.User;
public interface UserService extends IService<User> {
}
UserServiceImpl
package com.xyy.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xyy.mapper.UserMapper;
import com.xyy.pojo.User;
import com.xyy.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
UserMapper
package com.xyy.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xyy.pojo.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
jwttoken
package com.xyy.config.jwt;
import org.apache.shiro.authc.AuthenticationToken;
public class JwtToken implements AuthenticationToken {
/**
* 密钥
*/
private String base64EncodedSecretKey;
public JwtToken(String base64EncodedSecretKey) {
this.base64EncodedSecretKey = base64EncodedSecretKey;
}
@Override
public Object getPrincipal() {
return base64EncodedSecretKey;
}
@Override
public Object getCredentials() {
return base64EncodedSecretKey;
}
}
JwtUtil
package com.xyy.config.jwt;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xyy.pojo.User;
import io.jsonwebtoken.*;
import org.joda.time.DateTime;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* JWT工具类
*/
@Component
public class JwtUtil {
/**
* 过期时间 24 小时
*/
private static final long EXPIRE_TIME = 60 * 24 * 60 * 1000;
/**
* 密钥
*/
public static final String SECRET = "base64EncodedSecretKey";
/**
* 根据用户名和密码生成token
*/
public static String createToken(User user,String base64EncodedSecretKey,int expireMinutes) throws Exception{
return Jwts.builder()
.claim("username",user.getUsername())
.claim("password",user.getPassword())
.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
/**
*
* @param token
* @param publicKey
* @return
* @throws Exception
*/
private static Jws<Claims> parserToken(String token,String publicKey) throws Exception {
return Jwts.parser().setSigningKey(publicKey)
.parseClaimsJws(token);
}
/**
* 获取token中的用户信息
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @return 用户信息
* @throws Exception
*/
public static User getUserFromToken(String token, String publicKey) throws Exception {
Jws<Claims> claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
return new User(body.get("username").toString(),body.get("password").toString());
}
/**
* 验证token是否正确
* @param token
* @return
* @throws JsonProcessingException
*/
public static boolean verify(String token) throws JsonProcessingException {
String sign = null;
try{
System.out.println("校验token"+token);
//获取签名
sign = token.split("\\.")[2];
//获取载荷
Map<String,Object> JwtClaims = Jwts
.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
ObjectMapper mapper = new ObjectMapper();
//将载荷转化为字符串
String payload = mapper.writeValueAsString(JwtClaims);
//重新生成签名
JwtBuilder builder = Jwts.builder()
.setPayload(payload)
.signWith(SignatureAlgorithm.HS256,SECRET);
String newSign = builder.compact().split("\\.")[2];
if (!sign.equals(newSign)){
return false;
}
}catch (ExpiredJwtException e){
e.getMessage();
System.out.println("token过期");
}catch (SignatureException |MalformedJwtException m){
m.getMessage();
return false;
}
System.out.println("token校验正确");
return true;
}
}
jwtfilter
package com.xyy.config.jwt;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.Filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* :preHandle -> doFilterInternal -> executeLogin -> onLoginSuccess
*/
public class JWTFilter extends BasicHttpAuthenticationFilter implements Filter {
/**
* 判断用户是否想要登入。
* 检测header里面是否包含Authorization字段即可
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String authorization = req.getHeader("Authorization");
return authorization != null;
}
/**
*
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authorization = httpServletRequest.getHeader("Authorization");
JwtToken token = new JwtToken(authorization);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
getSubject(request, response).login(token);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginAttempt(request, response)) {
try {
executeLogin(request, response);
} catch (Exception e) {
response401(request, response);
}
}
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);
}
/**
* 将非法请求跳转到 /401
*/
private void response401(ServletRequest req, ServletResponse resp) {
try {
HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
httpServletResponse.sendRedirect("/401");
} catch (IOException e) {
e.getMessage();
}
}
}
自定义异常
package com.xyy.config.exception;
/**
* 自定义认证异常
*/
public class UnauthorizedException extends RuntimeException {
public UnauthorizedException(String msg) {
super(msg);
}
public UnauthorizedException() {
super();
}
}
全局异常处理
package com.xyy.config.exception;
import com.xyy.util.Response;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.servlet.http.HttpServletRequest;
import java.net.BindException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 全局异常处理
*/
@RestControllerAdvice
public class ExceptionController {
/**
* 捕捉所有Shiro异常
*/
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(ShiroException.class)
public Response handle401(ShiroException e) {
return new Response(401, "无权访问(Unauthorized):", e.getMessage());
}
/**
* 单独捕捉Shiro(UnauthorizedException)异常 该异常为访问有权限管控的请求而该用户没有所需权限所抛出的异常
*/
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(UnauthorizedException.class)
public Response handle401(UnauthorizedException e) {
return new Response(401, "无权访问(Unauthorized):当前Subject没有此请求所需权限",e.getMessage());
}
/**
* 单独捕捉Shiro(UnauthenticatedException)异常
* 该异常为以游客身份访问有权限管控的请求无法对匿名主体进行授权,而授权失败所抛出的异常
*/
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(UnauthenticatedException.class)
public Response handle401(UnauthenticatedException e) {
return new Response(401, "无权访问(Unauthorized):当前Subject是匿名Subject,请先登录(This subject is anonymous.)",null);
}
/**
* 捕捉校验异常(BindException)
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public Response validException(BindException e) {
return new Response(401, "bindException",null);
}
/**
* 捕捉校验异常(MethodArgumentNotValidException)
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Response validException(MethodArgumentNotValidException e) {
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
Map<String, Object> error = this.getValidError(fieldErrors);
return new Response(405, error.get("errorMsg").toString(), error.get("errorList"));
}
/**
* 捕捉404异常
*/
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NoHandlerFoundException.class)
public Response handle(NoHandlerFoundException e) {
return new Response(404, e.getMessage(),null);
}
/**
* 捕捉其他所有异常
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public Response globalException(HttpServletRequest request, Throwable ex) {
return new Response(1000, ex.toString() + ": " , 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);
}
/**
* 获取校验错误信息
*/
private Map<String, Object> getValidError(List<FieldError> fieldErrors) {
Map<String, Object> map = new HashMap<String, Object>(16);
List<String> errorList = new ArrayList<String>();
StringBuffer errorMsg = new StringBuffer("校验异常(ValidException):");
for (FieldError error : fieldErrors) {
errorList.add(error.getField() + "-" + error.getDefaultMessage());
errorMsg.append(error.getField() + "-" + error.getDefaultMessage() + ".");
}
map.put("errorList", errorList);
map.put("errorMsg", errorMsg);
return map;
}
}
MyRealm
package com.xyy.config.shiro;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.xyy.config.jwt.JwtToken;
import com.xyy.config.jwt.JwtUtil;
import com.xyy.mapper.UserMapper;
import com.xyy.pojo.User;
import org.apache.catalina.security.SecurityUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.authc.*;
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.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@Service
public class MyRealm extends AuthorizingRealm {
private static final Logger LOGGER = LogManager.getLogger(MyRealm.class);
@Autowired
private UserMapper userMapper;
/**
* 大坑!,必须重写此方法,不然Shiro会报错
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
/**
* 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
try{
User user = JwtUtil.getUserFromToken(principals.toString(),JwtUtil.SECRET);
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("username",user.getUsername());
queryWrapper.eq("password",user.getPassword());
User user1 = userMapper.selectOne(queryWrapper);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole(user1.getRole());
Set<String> permission = new HashSet<>(Arrays.asList(user1.getPermission().split("\\,")));
simpleAuthorizationInfo.addStringPermissions(permission);
return simpleAuthorizationInfo;
}catch (Exception e){
e.getMessage();
}
return null;
}
/**
* 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
// 解密获得username,用于和数据库进行对比
try{
User user = JwtUtil.getUserFromToken(token,JwtUtil.SECRET);
if (user == null) {
throw new AuthenticationException("非法token");
}
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("username",user.getUsername());
queryWrapper.eq("password",user.getPassword());
User user1 = userMapper.selectOne(queryWrapper);
if (user1 == null) {
throw new AuthenticationException("User didn't existed!");
}
if (! JwtUtil.verify(token)) {
throw new AuthenticationException("Username or password error");
}
return new SimpleAuthenticationInfo(token, token, "my_realm");
}catch (Exception e){
e.getMessage();
}
return null;
}
}
ShiroConfig
package com.xyy.config.shiro;
import com.xyy.config.jwt.JWTFilter;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.RedisTemplate;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;
/**
* shiro核心配置类
*/
@Configuration
public class ShiroConfig {
@Bean("securityManager")
public DefaultWebSecurityManager getManager(MyRealm userRealm, RedisTemplate redisTemplate) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(userRealm);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
manager.setSubjectDAO(subjectDAO);
return manager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean factory(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 添加自己的过滤器取名为jwt
Map<String, Filter> filterMap = new HashMap<>();
filterMap.put("jwtFilter", new JWTFilter());
factoryBean.setFilters(filterMap);
factoryBean.setSecurityManager(securityManager);
// 自定义url规则
Map<String, String> filterRuleMap = new HashMap<>();
// 所有请求通过我们自己的JWTFilter
filterRuleMap.put("/**", "jwtFilter");
factoryBean.setFilterChainDefinitionMap(filterRuleMap);
return factoryBean;
}
/**
* 下面的代码是添加注解支持
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// 强制使用cglib,防止重复代理和可能引起代理出错的问题,https://zhuanlan.zhihu.com/p/29161098
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
Controller
package com.xyy.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xyy.config.exception.UnauthorizedException;
import com.xyy.config.redis.RedisUtil;
import com.xyy.mapper.UserMapper;
import com.xyy.pojo.User;
import com.xyy.config.jwt.JwtUtil;
import com.xyy.util.Response;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
public class TestController {
private static final Logger LOGGER = LogManager.getLogger(TestController.class);
@Autowired
private UserMapper userMapper;
@PostMapping("/login")
public Response login(@RequestParam("username") String username,
@RequestParam("password") String password,
HttpServletResponse httpServletResponse) throws Exception{
QueryWrapper<User> queryWrapper = new QueryWrapper();
queryWrapper
.eq("username",username)
.eq("password",password);
User user = userMapper.selectOne(queryWrapper);
if (user.getPassword().equals(password)) {
return new Response(200, "登陆成功", "token为:"+ JwtUtil.createToken(user,JwtUtil.SECRET,30));
} else {
throw new UnauthorizedException();
}
}
@RequestMapping("test")
public Response hehe(HttpServletRequest h){
return new Response(200,h.getHeader("Authorization"),null);
}
@GetMapping("/article")
public Response article() {
Subject subject = SecurityUtils.getSubject();
System.out.println(subject.getPrincipal().toString());
if (subject.isAuthenticated()) {
return new Response(200, "You are already logged in", null);
} else {
return new Response(200, "You are guest", null);
}
}
@GetMapping("/require_auth")
@RequiresAuthentication
public Response requireAuth() {
return new Response(200, "You are authenticated", null);
}
@GetMapping("/require_role")
@RequiresRoles("admin")
public Response requireRole() {
return new Response(200, "You are visiting require_role", null);
}
@GetMapping("/require_permission")
@RequiresPermissions(logical = Logical.AND, value = {"view", "edit"})
public Response requirePermission() {
return new Response(200, "You are visiting permission require edit,view", null);
}
@RequestMapping(path = "/401")
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public Response unauthorized() {
return new Response(401, "Unauthorized", null);
}
}
使用postMan进行结果演示:
而后每一次请求都要在请求头里放入token,才能验证通过。
登陆过,拿着后台的token去登录,这里使用了注解进行判断
用户有角色admin的情况
使用user角色的会提示
有对应权限的访问
测试发现的问题:
如果使用错误的token去访问,会一直循环认证方法,在使用article这个url的时候
控制台打印了十几次的校验token。未解决这个错误,请各位测试的时候不要进行此操作