欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot集成Shiro\JWT

程序员文章站 2024-03-20 20:18:16
...

1.大体思路总结
shiro:权限验证
(1)需要自定义realm,realm中处理用户角色权限相关的东西.
(2)shiroConfig.java,shiro配置i相关
(3)loginController,登录处理
(4)拦截器,集成jwt后,对token进行校验
JWT:生成token的工具
(1)工具类:生成token,验证token等各种方法
(2)JWTToken.java,一个存token的类
2.上代码
(1)maven配置

		<!-- jwtToken -->
		<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
        <!-- shiro -->
  		<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

(2)shiroConfig

package com.lx.config;

import com.lx.filter.JwtTokenFilter;
import com.lx.shiro.CustomRealm;
import org.apache.shiro.authc.AuthenticationToken;
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.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class shiroConfig {

    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    //将自己的验证方式加入容器
    @Bean
    public CustomRealm myShiroRealm() {
        CustomRealm customRealm = new CustomRealm();
        return customRealm;
    }

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    @Bean
    public FilterRegistrationBean delegatingFilterProxy(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilterFactoryBean");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }
    
    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {//, JwtTokenFilter filter
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //错误页面,认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");

        Map<String, javax.servlet.Filter> mapFilter = new HashMap<String, Filter>();
        mapFilter.put("jwtFilter", new JwtTokenFilter());
        shiroFilterFactoryBean.setFilters(mapFilter);

        Map<String, String> map = new LinkedHashMap<String, String>();
        //登出
        map.put("/logout", "logout");
        //对所有用户认证
//        map.put("/**", "authc");
        map.put("/login","anon");
        //拦截器
        map.put("/**", "jwtFilter");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }
    
    //加入注解的使用,不加入这个注解不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

(3)realm
loginService.getUserByName是去数据库里查用户信息的

package com.lx.shiro;

import com.lx.bean.Permissions;
import com.lx.bean.Role;
import com.lx.bean.User;
import com.lx.config.JWTToken;
import com.lx.dao.User2;
import com.lx.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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private LoginService loginService;

//    @Override
//    public boolean supports(AuthenticationToken token) {
//        return token instanceof SimpleUsernameRealm;
//    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取登录用户名
        String name = (String) principalCollection.getPrimaryPrincipal();
        //根据用户名去数据库查询用户信息
        User2 user = loginService.getUserByName(name);
        //添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        for (Role role : user.getRoles2()) {
            //添加角色
            simpleAuthorizationInfo.addRole(role.getRoleName());
            //添加权限
            for (Permissions permissions : role.getPermissions()) {
                simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
            }
        }
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //加这一步的目的是在Post请求的时候会先进认证,然后在到请求
        if (authenticationToken.getPrincipal() == null) {
            return null;
        }
        //获取用户信息
        String name = authenticationToken.getPrincipal().toString();
        User2 user = loginService.getUserByName(name);
        if (user == null) {
            //这里返回后会报出对应异常
            return null;
        } else {
            //这里验证authenticationToken和simpleAuthenticationInfo的信息
            String name1 = getName();

            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), ByteSource.Util.bytes("salt"), name1);
            return simpleAuthenticationInfo;
        }
    }
}

(4)拦截器

package com.lx.filter;

import com.lx.utils.JWTUtil;
import com.sun.deploy.util.StringUtils;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import tk.mybatis.mapper.util.StringUtil;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

/**
 * @author: liuxu
 * @create: 2020-04-12 21:04
 */
@Component("jwtTokenFilter")
public class JwtTokenFilter extends FormAuthenticationFilter {

    /**
     * 如果时登录请求,返回true
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        if (isLoginRequest(servletRequest, servletResponse)) {
            if (isLoginSubmission(servletRequest, servletResponse)) {
                return true;
            }
        }else {
            throw new RuntimeException("token不存在,非法请求");
        }
        return false;
    }

    /**
     * 所有请求都会被拦截,当返回false时会调用onAccessDenied
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        Subject subject = this.getSubject(request, response);
        HttpServletRequest request1 = (HttpServletRequest) request;
        String token = request1.getHeader("X-Auth-Token");
        if (StringUtil.isEmpty(token)) {
            return false;
        }
        String[] perms = (String[]) mappedValue;
        boolean isPermitted = true;
        if (perms != null && perms.length > 0) {

        }
        boolean authenticated = subject.isAuthenticated();
        return authenticated;
    }
}

(5)登录测试

package com.lx.controller;

import com.lx.bean.User;
import com.lx.dao.User2;
import com.lx.service.LoginService;
import com.lx.utils.JWTUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
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.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class LoginController {

    @Autowired
    private LoginService loginService;

    @RequestMapping("/login")
    public String login(@RequestBody User user) {
        //添加用户认证信息
        Subject subject = SecurityUtils.getSubject();
        String token = "";
        try{
            token = JWTUtil.getToken(user.getUserName());
            System.out.println(token);
        }catch (Exception e ){
            e.printStackTrace();
        }
        String salt = "salt";
        Md5Hash md5Hash1 = new Md5Hash("1234");
        Md5Hash md5Hash2 = new Md5Hash("1234",salt);
        Md5Hash md5Hash3 = new Md5Hash("1234",salt, 1);
        Md5Hash md5Hash4 = new Md5Hash("1234",salt, 2);
        System.out.println(md5Hash1);
        System.out.println(md5Hash2);
        System.out.println(md5Hash3);
        System.out.println(md5Hash4);
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                user.getUserName(),
                md5Hash4.toString());
        try {
            //进行验证,这里可以捕获异常,然后返回对应信息
            subject.login(usernamePasswordToken);
            boolean admin = subject.isPermitted("add");

//           subject.checkRole("admin");
//           subject.checkPermissions("query", "add");
           System.currentTimeMillis();
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return "账号或密码错误!";
        } catch (AuthorizationException e) {
            e.printStackTrace();
            return "没有权限";
        }
        return token;
    }
    //注解验角色和权限
    @RequiresRoles("admin")
    @RequiresPermissions("add")
    @RequestMapping("/index")
    public String index() {
        return "index!";
    }

//    @RequiresRoles("admin")
//    @RequiresPermissions("add")
    @RequestMapping("/aaa")
    public String aaa() {
        List<User2> user = loginService.getUser();

        return "index!"+user.size();
    }
}

2.JWT
(1)JWTToken

package com.lx.config;

import org.apache.shiro.authc.AuthenticationToken;

/**
 * @author: liuxu
 * @create: 2020-04-11 21:30
 */
public class JWTToken implements AuthenticationToken {
    private static final long serialVersionUID = 1L;
    // 秘钥
    private String token;

    public JWTToken(String token) {
        this.token = token;
    }
    @Override
    public Object getPrincipal() {
        return token;
    }


    @Override
    public Object getCredentials() {
        return token;
    }

}

(2)jwt工具类

package com.lx.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: liuxu
 * @create: 2020-04-11 21:32
 */
@Component
public class JWTUtil {
    private static final long EXPIRATIONTIME = 604000000;
    private static final String SECRET = "ad5608bs32kzfwd48qfwgps157";
    private static final String TOKEN_PREFIX = "Bearer";
    private static final String HEADER_STRING = "Authorization";

    /**
     * 获取当前时间
     * @return
     */
    public static Date getCurrentDate() {
        return new Date(System.currentTimeMillis());
    }

    /**
     * 通过claims获取token中的到期时间
     * @param token
     * @return
     */
    public static Date getExpiration(String token) {
        Claims claims = JWTUtil.getClaim(token);
        if (claims == null) {
            return null;
        } else {
            return claims.getExpiration();
        }
    }

    /**
     * 通过claims获取token中的登录名
     * @param token
     * @return
     */
    public static String getUsername(String token) {
        Claims claims = JWTUtil.getClaim(token);
        if (claims == null) {
            return null;
        } else {
            return claims.get("username").toString();
        }
    }

    /**
     * 获取claims
     * @param token
     * @return
     */
    public static Claims getClaim(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * token校验
     * @param token
     * @param userName
     * @return
     */
    public static boolean verify(String token, String userName) {
        return userName.equals(JWTUtil.getUsername(token)) &&
                getExpiration(token).before(JWTUtil.getCurrentDate());
    }


    /**
     * 登录时生成token
     * @param username
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String getToken(String username) throws UnsupportedEncodingException {

        Map<String, Object> claims = new HashMap<String, Object>(1);
        claims.put("username", username);

        String token = Jwts.builder()
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        // 附带username信息
        return token;
    }
}

大体上就这么些吧~。过两天再试试注解权限验证