JWT生成token
- JWT生成Token
- 概要:
- 1、为什么使用JWT?
- 2、认证原理
- 3、 idea创建项目小demo测试步骤
- 测试demo目录结构
- 1、引入依赖
- 2、自定义两个注解
- 3、封装生成Token的工具类
- 4、新增一个自定义拦截器AuthenticationInterceptor
- 5、配置拦截器
- 6、验证
- 7、postman功能测试
- Strings.isEmpty()的使用
- Shiro(springBoot整合shiro)
- 1、什么是Shiro?
- 2、主要功能
- Shiro架构(外部)
- Shiro架构(内部)
- 3、可能出现的异常
- 4、springboot中集成shiro
- 1、创建一个springboot项目
- 2、整合步骤
- 1、添加依赖
- 2、创建ShiroConfig配置类
- 3、创建CustomRealm权限授权、认证类
- 4、实体类
- 5、创建模拟数据库查询Server类
- 6、controller层登录
- 7、错误提醒
- 8、postman测试工具测试
JWT生成Token
概要:
JWT意思是Json web token,通过POST参数或者在HTTP header发送,然后进行验证,验证通过之后,就能返回响应的资源给浏览器。
通常和权限框架(如shiro)搭配使用
1、为什么使用JWT?
1.简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快。
2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库。
3.安全(security): 与简单的JSON相比,XML和XML数字签名会引入复杂的安全漏洞。
2、认证原理
1.用户登陆之后,使用密码对账号进行签名生成并返回token并设置过期时间;
2.将token保存到本地,并且每次发送请求时都在header上携带token。
3.shiro过滤器拦截到请求并获取header中的token,并提交到自定义realm的doGetAuthenticationInfo方法。
4.通过jwt解码获取token中的用户名,从数据库中查询到密码之后根据密码生成jwt效验器并对token进行验证。
3、 idea创建项目小demo测试步骤
测试demo目录结构
1、引入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
2、自定义两个注解
1.用来跳过验证的PassToken
@Target({ElementType.METHOD,ElementType.TYPE})//可以给一个方法进行注解,可以给一个类进行注解
@Retention(RetentionPolicy.RUNTIME)//运行时注解
public @interface PassToken {
//该参数是否必须,如果true的话,当请求中没有传递该参数就报错。
boolean required() default true;
}
2.需要登录才能进行操作的注解UserLoginToken
@Target({ElementType.METHOD,ElementType.TYPE})//Target注解用途
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
//该参数是否必须,如果true的话,当请求中没有传递该参数就报错。
// 就是请求的url路径上如果需要参数的话是否必须传参(跟@RequestParam中的required是否需要参数一样),例:如果是/hello/{id}则必须传个id值
boolean required() default true;
}
3、封装生成Token的工具类
getToken用于根据userCode和userPassword生成签名(token)和密钥,
public class JwtUtil {
public static String getToken(String userCode,String userPassword){
//创建token
//Algorithm.HMAC256():使用HS256生成token,密钥则是用户的密码,唯一密钥的话可以保存在服务端。
//withAudience()存入需要保存在token的信息,这里把userCode存入token中 sign:签名
String token = JWT.create().withAudience(userCode).sign(Algorithm.HMAC256(userPassword));
return token;
}
}
4、新增一个自定义拦截器AuthenticationInterceptor
用于过滤后进行token认证判断,用户登录判断,体现两个自定义注解的作用
package com.hisoft.interceptor;
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.exceptions.JWTVerificationException;
import com.hisoft.annotation.PassToken;
import com.hisoft.annotation.UserLoginToken;
import com.hisoft.mapper.UserMapper;
import com.hisoft.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* 新增AuthenticationInterceptor拦截器,在controller访问需要进行token验证的路径则会进行拦截处理,如访问/hello则会先拦截判断
*/
public class AuthenticationInterceptor extends HandlerInterceptorAdapter {
@Autowired
private UserMapper userMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从http请求头中取出token
String token = request.getHeader("token");
//如果不是映射到方法直接通过(不是映射方法指的是没有说明请求路径的方法,如controller中的普通方法)
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();//获取请求的映射方法(也就是hello方法)
//检查映射方法是否有passToken注解,有则跳过认证,判断该请求映射方法上是否有passToken注解
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
//检查映射方法是否有@UserLoginToken注解,有则进行token认证对比
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
//执行认证
if (token == null) {
throw new RuntimeException("无token,请重新登录");
}
//获取token中的userCode
String userCode = "";
try {
userCode = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException e) {
throw new RuntimeException("token验证失败");
}
User user = userMapper.findUserByUserCode(userCode);
if(user == null){
throw new RuntimeException("用户不存在,请重新登录!");
}
//验证token verifier:校验机 Algorithm:算法
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getUserPassword())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("token验证失败");
}
return true;
}
}
return false;
}
}
5、配置拦截器
任何uri请求一但发送则进行拦截,然后进行逻辑处理,拦截后传入一个自定义的拦截器AuthenticationInterceptor,从而实现自定义token认证的逻辑功能
WebMvcConfigurer配置类其实是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制,可以自定义一些 Handler、Interceptor、ViewResolver、MessageConverter。基于java-based方式的spring mvc配置,需要创建一个配置类(@Configuration注解标识) 并实现 WebMvcConfigurer 接口
详细了解 : https://blog.csdn.net/qq_33375499/article/details/105203588?ops_request_misc=&request_id=&biz_id=102&utm_term=WebMvcConfigurer&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-105203588.pc_search_mgc_flag&spm=1018.2226.3001.4187
/**
* 配置拦截器,controller层中url访问先跑这个方法,然后到自定义拦截器里,然后再到controller具体方法
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册拦截规则 addInterceptor():需要传入一个拦截器,可以是自定义拦截规则
InterceptorRegistration interceptorRegistration = registry.addInterceptor(authenticationInterceptor());
//addPathPatterns("/**")所有的请求都拦截
InterceptorRegistration register = interceptorRegistration.addPathPatterns("/**");
//上一步拦截所有然后得到的也是一个InterceptorRegistration,调用excludePathPatterns方法指定可以放行的路径
register.excludePathPatterns("/login");
}
@Bean//AuthenticationInterceptor自定义的认证拦截器,自动配置并注入spring管理
public AuthenticationInterceptor authenticationInterceptor(){
return new AuthenticationInterceptor();
}
}
6、验证
不加注解的话默认不验证,登录接口一般是不验证的。在hello()中我加上了登录注解,说明该接口必须登录获取token后,在请求头中加上token并通过验证才可以访问
1. 不进行token验证
2. 进行token验证
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
/**不进行token验证
* 加注解@PassToken不进行token验证
*/
@PassToken
@PostMapping("/login")
public Object login(@RequestParam("userCode") String userCode,
@RequestParam("userPassword") String userPassword) {
User user = userMapper.findUserByUserCode(userCode);
Map map = new HashMap();
if(user.getUserPassword().equals(userPassword)){
map.put("status",1);
map.put("msg","success");
//JwtUtil.getToken()自定义的获取token静态工具类
map.put("token", JwtUtil.getToken(userCode,userPassword));
}else{
map.put("status",0);
map.put("msg","账号或密码错误");
}
return map;
}
/**进行token验证
* 加注解@UserLoginToken进行token验证
*/
@UserLoginToken
@GetMapping("/hello")
public Object hello(){
Map map = new HashMap();
map.put("name","张三");
map.put("birthday",new Date());
List<User> userList = userMapper.getUserList();
map.put("userList",userList);
return map;
}
}
UserMapper
模拟数据库查询,不是真正的数据库查询,方便测试
@Repository
public class UserMapper {
public User findUserByUserCode(String userCode) {
User user = new User();
user.setUserCode(userCode);
user.setUserPassword("123456");
return user;
}
public List<User> getUserList() {
List<User> userList = new ArrayList<>();
userList.add(new User("hello","111"));
userList.add(new User("hello","222"));
userList.add(new User("hello","333"));
userList.add(new User("hello","444"));
return userList;
}
}
User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String userCode;
private String userPassword;
}
7、postman功能测试
1.不需要验证token的登录测试
需要token验证的路径访问测试
后端获取token值
Strings.isEmpty()的使用
导入依赖
1.commons.lang3介绍
在Java开发过程中,处理字符串是一个很经常的事情,但是Java原生态的处理字符串的一些方法用不起并不是很优雅。apache基金会给我们提供了一个很强大的处理字符串的工具StringUtils,很好用!
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
public class TestString {
public static void main(String[] args) {
String str = null;
String hegx = " ";
System.out.println(StringUtils.isEmpty(hegx));//true
System.out.println(StringUtils.isEmpty(str));//true
}
}
因为我们不知道前端传过来的字符串是null还是“ ”空,所以Stringutils就派上用场了,两种都可以判断。
Shiro(springBoot整合shiro)
1、什么是Shiro?
Apache Shiro
是一个Java 的安全(权限)框架。Shiro
可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。Shiro
可以完成,认证,授权,加密,会话管理,Web集成,缓存等.
2、主要功能
1
Authentication:
身份认证、登录,验证用户是不是拥有相应的身份;
2Authorization:
授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行什么操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限!
3Session Manager:
会话管理,即用户登录后就是第-次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通的JavaSE环境,也可以是Web环境;
4Cryptography:
加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;
5Web Support:
Web支持,可以非常容易的集成到Web环境;
6Caching:
缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高效率
7Concurrency:
Shiro支持多线程应用的并发验证,即,如在-个线程中开启另-一个线程,能把权限自动的传
播过去
8Testing:
提供测试支持;
9RunAs:
允许一个用户假装为另-一个用户(如果他们允许)的身份进行访问;
10Remember Me:
记住我,这个是非常常见的功能,即一-次登录后, 下次再来的话不用登录了
Shiro架构(外部)
从外部来看Shiro,即从应用程序角度来观察如何使用shiro完成工作:
1 .
subject:
应用代码直接交互的对象是Subject, 也就是说Shiro的对外API核2心就是Subject, Subject代表了当前的用户,这个用户不-定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等,与Subject的所有交互都会委托给SecurityManager; Subject其实是一一个门面,
SecurityManageer
才是实际的执行者
2
SecurityManager:
安全管理器,即所有与安全有关的操作都会与SercurityManager交互, 并且它管理着所有的Subject,可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色
3
Realm:
Shiro从Realm获取安全数据 (如用户,角色,权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较,来确定用户的身份是否合法;也需要从Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行,可以把Realm看DataSource;
Shiro架构(内部)
1
Subject:
任何可以与应用交互的用户;
Security Manager:相当于SpringMVC中的DispatcherSerlet; 是Shiro的心脏, 所有具体的交互都通过Security Manager进行控制,它管理者所有的Subject, 且负责进行认证,授权,会话,及缓存的管理。
2Authentication:
认证,判断账号和密码的匹配。
3Authorization:
授权,点击按钮链接等调用资源的时候,判断是否有这个权限
4Realm:
可以有-一个或者多个的realm, 可以认为是安全实体数据源,即用于获取安全实体的,可以用JDBC实现,也可以是内存实现等等,由用户提供;所以- -般在应用中都需要实现自己的realm
5SessionManager:
管理Session生 命周期的组件,而Shiro并不仅仅可以用在Web环境,也可以用在普通的JavaSE环境中
6CacheManager:
缓存控制器,来管理如用户,角色,权限等缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能;
7Cryptography:
密码模块,Shiro 提高了一些常见的加密组件用于密码加密, 解密等
3、可能出现的异常
AuthenticationException
//所有shiro异常的父类UnknownAccountException
//账号不存在异常IncorrectCredentialsException
//密码不正确LockedAccountException
//账号被锁定异常
4、springboot中集成shiro
需要两个类:一个是shiroConfig类,一个是CustomRealm类。
ShiroConfig类:
顾名思义就是对shiro的一些配置,相当于之前的xml配置。包括:过滤的文件和权限,密码加密的算法,其用注解等相关功能。CustomRealm类:
自定义的CustomRealm继承AuthorizingRealm。并且重写父类中的doGetAuthorizationInfo(权限配置)、doGetAuthenticationInfo(身份认证)这两个方法,然后自定义自己权限和认证
1、创建一个springboot项目
项目目录结构
2、整合步骤
1、添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
2、创建ShiroConfig配置类
package com.hisoft.config;
import com.hisoft.shiro.CustomRealm;
import org.apache.shiro.mgt.SecurityManager;
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.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* ShiroConfig配置类执行的顺序:项目一启动便直接执行shiro配置类
* 1、defaultAdvisorAutoProxyCreator开启Aop动态代理
* 2、SecurityManager权限管理
* 3、将自己的验证方式加入容器
* 4、将配置好的权限管理器注入权限管理
* 5、shiro的过滤工厂
*/
@Configuration//标注配置类,相当于spring中的xml配置文件
public class ShiroConfig {
//开启AOP动态代理
@Bean//目的是使容器能扫描到shiro的注解
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {//创建Aop代理
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
//将自己的验证方式加入容器
@Bean
public CustomRealm myShiroRealm() {
return new CustomRealm();
}
//权限管理,配置主要是Realm的管理认证,安全管理器SecurityManager
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());//让安全管理器使用我们的自定义realm
return securityManager;
}
//写一个shiro的过滤Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
//配置不会被拦截的链接
//登出
map.put("/logout", "logout");//logout代表如果访问的是/logout的话则退出登录
//对所有用户认证
map.put("/**", "authc");//authc代表所有路径都需要认证,写死的
//配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
shiroFilterFactoryBean.setLoginUrl("/login");
//登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//错误页面,认证不通过跳转的链接
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
//传入过滤的条件后台跳转链接进行设置,传入的是一个map集合
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;//返回shiro过滤工厂
}
//将配置好的安全管理器security注入权限管理 advisor:顾问
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);//注入权限管理器
return authorizationAttributeSourceAdvisor;
}
}
3、创建CustomRealm权限授权、认证类
package com.hisoft.shiro;
import com.hisoft.bean.Permissions;
import com.hisoft.bean.Role;
import com.hisoft.bean.User;
import com.hisoft.service.LoginService;
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.util.StringUtils;
/**
* 继承AuthorizingRealm重写认证与授权功能,自定义自己的认证授权规则
*/
public class CustomRealm extends AuthorizingRealm {
@Autowired
private LoginService loginService;
/**
* @MethodName doGetAuthorizationInfo
* @Description 权限配置类
* @Param [principalCollection]
* @Return AuthorizationInfo
* @Author hisoft
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取登录用户名
String name = (String) principalCollection.getPrimaryPrincipal();
//查询用户对象
User user = loginService.getUserByName(name);
//添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (Role role : user.getRoles()) {
//添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
//添加权限
for (Permissions permissions : role.getPermissions()) {
simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
}
}
return simpleAuthorizationInfo;
}
/**
* @MethodName doGetAuthenticationInfo
* @Description 认证配置类
* @Param [authenticationToken]
* @Return AuthenticationInfo
* @Author hisoft
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//getPrincipal()获取存储的principal(委托人),判断是否存在
if (StringUtils.isEmpty(authenticationToken.getPrincipal())) {
return null;
}
//获取用户信息
String name = authenticationToken.getPrincipal().toString();
User user = loginService.getUserByName(name);
if (user == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword(), getName());
return simpleAuthenticationInfo;
}
}
}
4、实体类
User、Role、Permissions
@Data
@AllArgsConstructor
public class Permissions {//权限类
private String id;
private String permissionsName;
}
*******************
@Data
@AllArgsConstructor
public class Role {//角色类
private String id;
private String roleName;
/**
* 角色对应权限集合
*/
private Set<Permissions> permissions;
}
****************
@Data
@AllArgsConstructor
public class User {
private String id;
private String userName;
private String password;
/**
* 用户对应的角色集合
*/
private Set<Role> roles;
}
5、创建模拟数据库查询Server类
也可以建个数据库进行测试,这里是为了便于测试接口
public interface LoginService{
User getUserByName(String userName);
}
实现类
@Service
public class LoginServiceImpl implements LoginService {
@Override
public User getUserByName(String getMapByName) {
return getMapByName(getMapByName);
}
/**
* 模拟数据库查询
* @param userName 用户名
* @return User
*/
private User getMapByName(String userName) {
Permissions permissions1 = new Permissions("1", "query");//设置拥有查询权限
Permissions permissions2 = new Permissions("2", "add");
Set<Permissions> permissionsSet = new HashSet<>();
permissionsSet.add(permissions1);
permissionsSet.add(permissions2);
Role role = new Role("1", "admin", permissionsSet);//设置角色admin拥有的权限
Set<Role> roleSet = new HashSet<>();
roleSet.add(role);
User user = new User("1", "hisoft", "123456", roleSet);//设置用户担任的角色
Map<String, User> map = new HashMap<>();
map.put(user.getUserName(), user);
Set<Permissions> permissionsSet1 = new HashSet<>();
permissionsSet1.add(permissions1);
Role role1 = new Role("2", "user", permissionsSet1);
Set<Role> roleSet1 = new HashSet<>();
roleSet1.add(role1);
User user1 = new User("2", "zhangsan", "123456", roleSet1);
map.put(user1.getUserName(), user1);
return map.get(userName);
}
}
6、controller层登录
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class LoginController {
@GetMapping("/login")
public String login(User user) {
if (StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassword())) {
return "请输入用户名和密码!";
}
//用户认证信息 获取subject,应用程序和shiro框架交互的门面,
// 然后与subject的所有交互都委托给SecurityManager(管理各个组件相当于springMvc中得是DispatcherServlet角色)
Subject subject = SecurityUtils.getSubject();
//shiro框架提供的UsernamePasswordToken是用来存储用户和密码的
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
user.getUserName(),
user.getPassword()
);
try {
//进行验证,这里可以捕获异常,然后返回对应信息,调用login方法,然后再内部进行一系列的操作最后执行到
//自定义的权限配置类CustomRealm中重写的doGetAuthorizationInfo方法
subject.login(usernamePasswordToken);
// subject.checkRole("admin");
// subject.checkPermissions("query", "add");
} catch (UnknownAccountException e) {
log.error("用户名不存在!", e);
return "用户名不存在!";
} catch (AuthenticationException e) {
log.error("账号或密码错误!", e);
return "账号或密码错误!";
} catch (AuthorizationException e) {
log.error("没有权限!", e);
return "没有权限";
}
return "login success";
}
@RequiresRoles("admin")//@RequiresRoles权限注解,代表需要admin权限
@GetMapping("/admin")
public String admin() {
return "admin success!";
}
@RequiresPermissions("query")
@GetMapping("/index")
public String index() {
return "index success!";
}
@RequiresPermissions("add")
@GetMapping("/add")
public String add() {
return "add success!";
}
}
7、错误提醒
@ControllerAdvice
@Slf4j
public class MyExceptionHandler {
@ExceptionHandler
public String ErrorHandler(AuthorizationException e) {
log.error("没有通过权限验证!", e);
return "my_error";//全局异常,如果报错误异常则自动跳转my_error页面
}
}
8、postman测试工具测试
登录认证测试
1 /add访问测试,zhangsan具有add权限访问成功
2 /index访问测试,zhangsan没有query权限,访问失败