SpringBoot+Shiro+JWT

 JWT

JWT文章:JWT详解_baobao555#的博客-CSDN博客_jwt

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。

在本项目中,我们规定每次请求时,需要在请求头中带上 token ,通过 token 检验权限,如没有,则说明当前为未登录状态

Shiro

Shiro - Shiro简介;Shiro与Spring Security区别;Spring Boot集成Shiro_MinggeQingchun的博客-CSDN博客_shiro

1,登录

/**
 * 登陆
 *
 * @param username 用户名
 * @param password 密码
 */
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResultMap login(String username, String password) {
    String realPassword = userMapper.getPassword(username);
    if (realPassword == null) {
        return resultMap.fail().code(401).message("用户名错误");
    } else if (!realPassword.equals(password)) {
        return resultMap.fail().code(401).message("密码错误");
    } else {
        //账号信息认证通过后调用JWT工具类创建Token并返回给前端
        return   resultMap.success().code(200).message(JWTUtil.createToken(username));
    }
}

2,ShiroConfig配置

 

/**
 * 先走 filter ,然后 filter 如果检测到请求头存在 token,则用 token 去 Shiro,走 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");


    // 访问 /unauthorized/**,/login,不通过JWTFilter
    filterRuleMap.put("/unauthorized/**", "anon");
    filterRuleMap.put("/login", "anon");
    factoryBean.setFilterChainDefinitionMap(filterRuleMap);
    return factoryBean;
}
/**
 * 自定义身份认证 realm,注入 securityManager
 */
@Bean
public SecurityManager securityManager(CustomRealm customRealm) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    // 设置realm.
    securityManager.setRealm(customRealm);
    DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
    DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
    defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
    subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
    securityManager.setSubjectDAO(subjectDAO);
    return securityManager;
}

  3,CustomRealm配置

@Component
public class CustomRealm extends AuthorizingRealm {
    @Autowired
    private UserMapper userMapper;
    /**
     * 必须重写此方法,不然会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("————权限认证————");
        String username = JWTUtil.getUsername(principals.toString());
        SimpleAuthorizationInfo  info = new SimpleAuthorizationInfo();
        //获得该用户角色
        String role = userMapper.getRole(username);
        //每个角色拥有默认的权限
        String rolePermission = userMapper.getRolePermission(username);
        //每个用户可以设置新的权限
        String permission = userMapper.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;
    }
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    System.out.println("————身份认证方法————");
    String token = (String)authenticationToken.getCredentials();
    String userName= JWTUtil.getUsername(token);
    if(userName==null || !JWTUtil.verify(token, userName)){
        throw new AuthenticationException("token认证失败!");
    }
    // 从数据库获取对应用户名密码的用户
    //String password =getPassword(token.getUsername());
    String password =userMapper.getPassword(userName);
    if (null == password) {
        throw new AuthenticationException("该用户不存在!");
    }
    int ban =userMapper.checkUserBanStatus(userName);
    if (ban == 1) {
        throw new AuthenticationException("该用户已被封号!");
    }
    return new SimpleAuthenticationInfo(token, token, "MyRealm");
}

 

4,JWT过滤器配置

/**
 * 判断用户是否想要登入。
 * 检测 header 里面是否包含 Token 字段
 */
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
    HttpServletRequest req = (HttpServletRequest) request;
    String token = req.getHeader("Token");
    return token != null;
}

 

/**
 * 如果带有 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 错误
            e.printStackTrace();
            //responseError(response, e.getMessage());
        }
    }
    //没有token无法通过过滤器。如果是登录接口应该在ShiroConfig那块放行不经过过滤器
    return false;
}
/**
 * 执行登陆操作,即执行CustomRealmdo.GetAuthenticationInfo()身份认证权限
 */
@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;
}

 

5,权限配置

      角色权限注解:@RequiresRoles     权限配置注解:@RequiresPermissions

     经过JWTFilter过滤器和用户身份校验后就进入CustomRealm的权限校验模块

/**
 * 拥有 vip 权限可以访问该页面
 */
@GetMapping("/getVipMessage")
@RequiresRoles(logical = Logical.OR, value = {"admin"})
@RequiresPermissions("vip")
public ResultMap getVipMessage() {
    try {
        return resultMap.success().code(200).message("成功获得 vip 信息!");
    }catch (Exception e) {
        e.printStackTrace();
    }
    return resultMap.success().code(201).message("获得 vip 信息失败!");
}

6,源码地址 

https://gitee.com/lzlgdx/practical-cases.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值