spring boot Shiro JWT integrity

A api to support H5, PC and APP three front-end, if you use session if not very friendly app, and the session in question cross-domain attacks, so I chose JWT

1. Import dependencies

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.2.0</version>
</dependency>

 

2. Custom JWTToken

import org.apache.shiro.authc.AuthenticationToken;

public class JwtToken implements AuthenticationToken {

    private String token;

    public JwtToken(String token) {
        this.token = token;
    }

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

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

 

Tools

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.interfaces.DecodedJWT;

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

public class JwtUtils {
    
    // 过期时间30天
    private static final long EXPIRE_TIME = 24 * 60 * 30 * 1000;

    /**
     * 校验token是否正确
     *
     * @param token    密钥
     * @param username 登录名
     * @param password 密码
     * @return
     */
    public static boolean verify(String token, String username, String password) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(password);

            JWTVerifier verifier = JWT.require(algorithm).withClaim("userName", username).build();

            DecodedJWT jwt = verifier.verify(token);

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取登录名
     *
     * @param token
     * @return
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);

            return jwt.getClaim("userName").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 生成签名
     *
     * @param username
     * @param password
     * @return
     */
    public static String sign(String username, String password) {
        try {
            // 指定过期时间
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);

            Algorithm algorithm = Algorithm.HMAC256(password);

            return JWT.create()
                    .withClaim("userName", username)
                    .withExpiresAt(date)
                    .sign(algorithm);
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }

}

 

3. Custom realm

import com.system.authorization.model.JwtToken;
import com.system.authorization.model.MzUser;
import com.system.authorization.service.MzUserService;
import com.system.authorization.utils.JwtUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Set;

public class JwtShiroRealm extends AuthorizingRealm {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private MzUserService mzUserService;

    /**
     * 使用JWT代替原生Token
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    //权限验证
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("doGetAuthorizationInfo:" + principalCollection.toString());

        String userName = JwtUtils.getUsername(principalCollection.toString());

        //获取权限数据
        Set<String> permissions = mzUserService.getPermissionByUserName(userName);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setStringPermissions(permissions);
        return simpleAuthorizationInfo;
    }

    / ** 
     * Authentication: Authentication is used to verify user identity 
     * Use this method to verify that the default user name is correct or not, throw an exception error 
     * / 
    @Override 
    protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken AuthenticationToken) throws AuthenticationException { 
        String token = authenticationToken.getPrincipal () .toString (); 

        System.out.println ( "Realm verification:" + token); 
        String the userName = JwtUtils.getUsername (token); 

        System.out.println ( "Realm verify the user name:" + the userName); 
        MzUser mzUser = mzUserService.queryByUserName (the userName);
         IF(mzUser == null ) {
             the throw  new new of AuthenticationException ( "token verification failure, insufficient authority" ); 
        } 

        IF (! {JwtUtils.verify (token, the userName, mzUser.getPassword ()))
             the throw  new new UnknownAccountException ( "token validation fails, insufficient permissions " ); 
        } 

        return  new new SimpleAuthenticationInfo (token, token," realm " ); 
    } 
}

 

4. Custom filter

import com.system.authorization.model.JwtToken;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
importjava.io.IOException;
 
public  class JwtAuthFilter the extends BasicHttpAuthenticationFilter { 

    Private Logger Logger = LoggerFactory.getLogger ( the this .getClass ()); 

    // login ID 
    Private  static String LOGIN_SIGN = "the auth-token-X" ; 

    / ** 
     * Detection user whether the login 
     * which detects whether the Authorization header field to 
     * 
     * @param Request 
     * @param the Response 
     * @return 
     * / 
    @Override 
    protected  boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpRequest = WebUtils.toHttp(request);

        String authorization = httpRequest.getHeader(LOGIN_SIGN);

        return StringUtils.isNoneBlank(authorization);
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpRequest = WebUtils.toHttp(request);
        String token = httpRequest.getHeader(LOGIN_SIGN);
        JwtToken jwtToken = newJwtToken (token);
         // submitted to log in to the realm, if the error would be afraid proudly abnormal and captured, if not Throws an exception to true 
        getSubject (Request, the Response) your .login (jwtToken);
         return  to true ; 
    } 

    @Override 
    protected  Boolean isAccessAllowed (the ServletRequest request, Response the ServletResponse, Object mappedValue) { 
        System.out.println ( "start jwt check" );
         // if not login request 
        IF (isLoginAttempt (request, Response)) {
             the try { 
                the executeLogin (request, Response); 
            } the catch (Exception E) {
 //                throw new TSharkException("登录权限不足!", e);
                throw new UnknownAccountException("token验证失败,权限不足");
            }
        }
        System.out.println("jwt 校验通过");
        return true;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("file application / JSON; charset = UTF-. 8" ); 
        httpResponse.setStatus (org.apache.http.HttpStatus.SC_UNAUTHORIZED); 
        System.out.println ( "token validation fails, no access" );
         return  to false ; 
    } 

    / ** 
     * support for cross-domain 
     * 
     * @param Request 
     * @param Response 
     * @return 
     * @throws Exception
      * / 
    @Override 
    protected  Boolean The preHandle (the ServletRequest Request, the 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);
    }

}

 

Authorization filter

import org.apache.http.HttpStatus;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RolesAndPermissionFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws{Exception 
        System.out.println ( "Start Roles permission check" );
         // Get Interface request address 
        String path = WebUtils.toHttp (Request) .getRequestURI (); 

        the Subject Subject = the getSubject (Request, Response); 

        // database request address is stored in the interface, where the interface address of the current request to verify whether the currently logged in user exists and, if it is verified 
        IF (subject.isPermitted (path))
             return  to true ; 
        System.out.println ( "Roles permission verification failed " );
         return  to false ; 
    } 

    @Override 
    protected  Boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setStatus(HttpStatus.SC_UNAUTHORIZED);
        return false;
    }
}

 

The configuration information, the injection spring containers

import com.system.authorization.filter.JwtAuthFilter;
import com.system.authorization.filter.RolesAndPermissionFilter;
import com.system.authorization.realm.JwtShiroRealm;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
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.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

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

@Configuration
@ConditionalOnWebApplication
public class ShiroConfig {

    @Bean
    public Realm jwtShiroRealm() {
        return new JwtShiroRealm();
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(jwtShiroRealm());

        // 关闭自带session
        DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
        evaluator.setSessionStorageEnabled(false);

        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        subjectDAO.setSessionStorageEvaluator(evaluator);

        defaultSecurityManager.setSubjectDAO(subjectDAO);

        return defaultSecurityManager;
    }


    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

        //将自定义的过滤器注入
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        filterMap.put("jwt", new new JwtAuthFilter ()); 
        filterMap.put ( "permission", new new RolesAndPermissionFilter ()); 

        factoryBean.setFilters (filterMap); 
        factoryBean.setSecurityManager (securityManager); 

        // filter rules defined 
        the Map <String, String> = filterRuleMap new new the HashMap < > ();
         // All requests must go through jwt, permission filter 
        filterRuleMap.put ( "/ **", "jwt, permission" );
         // the login interface can not verify 
        filterRuleMap.put ( "/ mz / the User / the Login "," anon " ); 

        factoryBean.setFilterChainDefinitionMap (filterRuleMap); 

        // set the login page, home page, page validation fails
        factoryBean.setLoginUrl("https://www.baidu.com");
        factoryBean.setSuccessUrl("https://www.cnblogs.com/gyli20170901/");
        factoryBean.setUnauthorizedUrl("/403");

        return factoryBean;
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

 

Reference: https://yq.aliyun.com/articles/646440

Guess you like

Origin www.cnblogs.com/gyli20170901/p/11263970.html