Spring使用JWT生成token、shiro

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)

原文链接:https://blog.csdn.net/qq_40649503/article/details/109481354

1、什么是Shiro?

Apache Shiro是一个Java 的安全(权限)框架。
Shiro可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JavaEE环境。
Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等.

2、主要功能

1 Authentication:身份认证、登录,验证用户是不是拥有相应的身份;
2 Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行什么操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限!
3 Session Manager:会话管理,即用户登录后就是第-次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通的JavaSE环境,也可以是Web环境;
4 Cryptography: 加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;
5 Web Support:Web支持,可以非常容易的集成到Web环境;
6 Caching:缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高效率
7 Concurrency: Shiro支持多线程应用的并发验证,即,如在-个线程中开启另-一个线程,能把权限自动的传
播过去
8 Testing:提供测试支持;
9 RunAs:允许一个用户假装为另-一个用户(如果他们允许)的身份进行访问;
10 Remember 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, 且负责进行认证,授权,会话,及缓存的管理。
2 Authentication: 认证,判断账号和密码的匹配。
3 Authorization: 授权,点击按钮链接等调用资源的时候,判断是否有这个权限
4 Realm:可以有-一个或者多个的realm, 可以认为是安全实体数据源,即用于获取安全实体的,可以用JDBC实现,也可以是内存实现等等,由用户提供;所以- -般在应用中都需要实现自己的realm
5 SessionManager:管理Session生 命周期的组件,而Shiro并不仅仅可以用在Web环境,也可以用在普通的JavaSE环境中
6 CacheManager: 缓存控制器,来管理如用户,角色,权限等缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能;
7 Cryptography:密码模块,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权限,访问失败
在这里插入图片描述

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值