springboot整合shiro框架详解

在ShiroRealm 中 对所有 引入的service 加上注解 @Lazy ,防止 事务回滚失败。具体原因看该文章

新增整合swagger2,因为之前整合了shiro,所以再访问swagger的时候总是被拦截导致无法访问,因此在ShiroConfiguration配置文件中,放开对swagger的拦截

新增CORS跨域配置。
待解决问题:前端利用vue传sessionid,后台通过shiro接收一直接收不到,后前端改成jQuery才可以,不知道问题出在哪

新增@RequiresRoles角色控制,个人感觉@RequiresRoles角色控制,属于粗粒度,@RequiresPermissions属于细粒度,因为@RequiresPermissions能给每个接口定义不同的权限
如下:
@RequiresRoles(value = “admin”)
@RequiresPermissions(“user:updateSysUser”)
两者属于and 的关系,同时添加注解,会先验证此接口是否有规定的角色,然后验证是否有该权限,必须同时满足才会通过认证

@RequiresPermissions多权限是分两种的,这里要注意

第一种:必须全部符合(默认不写或者在后面添加logical = Logical.AND)
@RequiresPermissions(value={“studentMan:find_record_list”,“teacher:find_record_list”})
上面这种情况是默认当前对象必须同时全部拥有指定权限

第二种:符合其中一个即可(logical = Logical.OR)
@RequiresPermissions(value={“studentMan:find_record_list”,“teacher:find_record_list”},logical=Logical.OR)
上面这种情况则是只要有其中一个权限即可访问

pom文件

		//shiro
		<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-cas</artifactId>
            <version>1.2.3</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!--集成swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>

用户工具类–主要获取当前登录用户的信息

	package rongheng.member.utils;
	import org.apache.commons.codec.binary.Hex;
	import org.apache.commons.lang3.StringEscapeUtils;
	import org.apache.commons.lang3.Validate;
	import org.apache.shiro.SecurityUtils;
	import org.apache.shiro.session.InvalidSessionException;
	import org.apache.shiro.session.Session;
	import org.apache.shiro.subject.Subject;
	import rongheng.member.entity.SysUser;
	import rongheng.member.mapper.UserMapper;
	import java.security.GeneralSecurityException;
	import java.security.MessageDigest;
	import java.security.SecureRandom;
	import java.text.DecimalFormat;
	public class UserUtils {
		
		private static UserMapper userMapper = SpringContextHolder.getBean(UserMapper.class);
		private static DecimalFormat  df = new DecimalFormat("############0.00");
		/**
		 * 根据ID获取用户
		 * @param userid
		 * @return 取不到返回null
		 */
		public static SysUser get(String userid){
			SysUser sysUser = userMapper.get(userid);
			if (sysUser == null){
				return null;
			}
			return sysUser;
		}
		/**
		 * 新的获取当前登录用户的对象
		 * @return
		 */
		public static SysUser getSysUser(){
			SysUser sysUser = (SysUser) SecurityUtils.getSubject().getPrincipal();
			return sysUser;
		}
		
		public static Session getSession(){
			try{
				Subject subject = SecurityUtils.getSubject();
				Session session = subject.getSession(false);
				if (session == null){
					session = subject.getSession();
				}
				if (session != null){
					return session;
				}
	//			subject.logout();
			}catch (InvalidSessionException e){
				
			}
			return null;
		}
	
		/**
		 * 生成安全的密码,生成随机的16位salt并经过1024次 sha-1 hash
		 */
		public String entryptPassword(String plainPassword) {
			String plain = unescapeHtml(plainPassword);
			byte[] salt = generateSalt(8);
			byte[] hashPassword = sha1(plain.getBytes(), salt, 1024);
			return encodeHex(salt)+encodeHex(hashPassword);
		}
		/**
		 * Hex编码.
		 */
		public String encodeHex(byte[] input) {
			return new String(Hex.encodeHex(input));
		}
	
		/**
		 * Html 解码.
		 */
		public String unescapeHtml(String htmlEscaped) {
			return StringEscapeUtils.unescapeHtml4(htmlEscaped);
		}
		/**
		 * 生成随机的Byte[]作为salt.
		 *
		 * @param numBytes byte数组的大小
		 */
		public byte[] generateSalt(int numBytes) {
			SecureRandom random = new SecureRandom();
			Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", numBytes);
			byte[] bytes = new byte[numBytes];
			random.nextBytes(bytes);
			return bytes;
		}
	
	
		public byte[] sha1(byte[] input, byte[] salt, int iterations) {
			return digest(input,"SHA-1", salt, iterations);
		}
	
		/**
		 * 对字符串进行散列, 支持md5与sha1算法.
		 */
		private byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) {
			try {
				MessageDigest digest = MessageDigest.getInstance(algorithm);
	
				if (salt != null) {
					digest.update(salt);
				}
	
				byte[] result = digest.digest(input);
	
				for (int i = 1; i < iterations; i++) {
					digest.reset();
					result = digest.digest(result);
				}
				return result;
			} catch (GeneralSecurityException e) {
				throw new RuntimeException(e);
			}
		}
	
		//double四舍五入保留两位小数 返回字符串
	    public static String doubleBackTwoString(double number){
	    	return df.format(number);
	    }
	    //double四舍五入保留两位小数    0.01是double类型的  又会变长
	    public static double doubleBackTwo(double number){
	    	return Double.parseDouble(df.format(number));
	    }
	}

自定义DefaultWebSessionManager

主要目的通过sessionid验证用户的登录状态

	package rongheng.member.session;

	import java.io.Serializable;
	import javax.servlet.ServletRequest;
	import javax.servlet.ServletResponse;
	import javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
	import org.apache.commons.lang3.StringUtils;
	import org.apache.shiro.session.Session;
	import org.apache.shiro.session.UnknownSessionException;
	import org.apache.shiro.session.mgt.SessionKey;
	import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
	import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
	import org.apache.shiro.web.util.WebUtils;
	/**
	 *  自定义WEB会话管理类
	 */
	public class SessionManager  extends DefaultWebSessionManager {
	
		public SessionManager() {
			super();
		}
		
		@Override
	    public Serializable getSessionId(SessionKey key) {
	        Serializable sessionId = key.getSessionId();
	        if(sessionId == null){
	            HttpServletRequest request = WebUtils.getHttpRequest(key);
	            HttpServletResponse response = WebUtils.getHttpResponse(key);
	            sessionId = this.getSessionId(request,response);
	        }
	        HttpServletRequest request = WebUtils.getHttpRequest(key);
	        request.setAttribute("sessionid",sessionId ==null?"":sessionId.toString());
	        return sessionId;
	    }
	
		@Override
		protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
			String sid = request.getParameter("sessionid");
	//		String sid = WebUtils.toHttp(request).getHeader("sessionid");
		if (StringUtils.isNotBlank(sid)) {
				// 设置当前session状态
	            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
	            ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); // session来源与url
	            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sid);
	            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
	        	return sid;
			}else{
				Serializable sessionId = super.getSessionId(request, response);			
				return sessionId;
			}
		}
		@Override
		protected Session retrieveSession(SessionKey sessionKey) {
			try{
				return super.retrieveSession(sessionKey);
			}catch (UnknownSessionException e) {
	    		// 获取不到SESSION不抛出异常
				return null;
			}
		}
	  }

自定义配置文件 ShiroConfiguration

	import org.apache.shiro.mgt.SecurityManager;
	import org.apache.shiro.session.mgt.SessionManager;
	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.web.servlet.FilterRegistrationBean;
	import org.springframework.context.annotation.Bean;
	import org.springframework.context.annotation.Configuration;
	import org.springframework.context.annotation.DependsOn;
	import org.springframework.web.filter.DelegatingFilterProxy;
	import rongheng.member.realm.FormAuthenticationFilter;
	import rongheng.member.realm.ShiroRealm;
	import javax.servlet.Filter;
	import java.util.LinkedHashMap;
	import java.util.Map;
	
	@Configuration
	public class ShiroConfiguration {
	    //将自己的验证方式加入容器
	    @Bean
	    public ShiroRealm shiroRealm() {
	        ShiroRealm shiroRealm = new ShiroRealm();
	        return shiroRealm;
	    }
	    //权限管理,配置主要是Realm的管理认证
	    @Bean
	    public DefaultWebSecurityManager securityManager() {
	        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
	        securityManager.setRealm(shiroRealm());
	        securityManager.setSessionManager(sessionManager());
	        return securityManager;
	    }

	    @Bean
	    public SessionManager sessionManager(){
	        return  new rongheng.member.session.SessionManager();
	    }
   	 //Filter工厂,设置对应的过滤条件和跳转条件
	    @Bean("shiroFilter")
	    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
	        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
	        //设置安全管理器
	        shiroFilterFactoryBean.setSecurityManager(securityManager);
	        //默认跳转到登陆
	        shiroFilterFactoryBean.setLoginUrl("/main/login");
	       //自定义过滤器
	        Map<String, Filter> filterMap=new LinkedHashMap<>();
	        //此处是被拦截的接口所需要访问的类,这里是自己去重新定义的
	        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
	        shiroFilterFactoryBean.setFilters(filterMap);
	        filterMap.put("authc",formAuthenticationFilter);
	        Map<String,String> filterChainDefinitionMap=new LinkedHashMap<>();
	        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
	        filterChainDefinitionMap.put("/loginOut", "logout");
	        //authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
	        //main 是指controller层的接口
	        filterChainDefinitionMap.put("/main/**","anon");
	         /*放开对swagger的拦截*/
	        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
	        filterChainDefinitionMap.put("/swagger/**", "anon");
	        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
	        filterChainDefinitionMap.put("/v2/**", "anon");
	        filterChainDefinitionMap.put("/webjars/**", "anon");
	        filterChainDefinitionMap.put("/configuration/**", "anon");
	        
	  	    //其他所有接口都需要认证,也就是需要之前输入过账号密码登录过
	        //过滤链定义,从上向下顺序执行,一般将/**放在最为下边 
	        //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
	        filterChainDefinitionMap.put("/**", "authc");
	        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
	        return shiroFilterFactoryBean;
	    }
	    /**
	     *  开启shiro aop注解支持.
	     *  使用代理方式;所以需要开启代码支持;否则@RequiresRoles等注解无法生效
	     * @param securityManager
	     * @return
	     */
	    @Bean
	    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager 				securityManager){
	        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
	        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
	        return authorizationAttributeSourceAdvisor;
	    }
	    /**
	     * Shiro生命周期处理器
	     * @return
	     */
	    @Bean
	    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
	        return new LifecycleBeanPostProcessor();
	    }
	    /**
	     * 自动创建代理
	     * @return
	     */
	    @Bean
	    @DependsOn({"lifecycleBeanPostProcessor"})
	    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
	        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
	        advisorAutoProxyCreator.setProxyTargetClass(true);
	        return advisorAutoProxyCreator;
	    }
	    //对应web.xml中对应的shiro过滤
	    @Bean
	    public FilterRegistrationBean delegatingFilterProxy(){
	        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
	        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
	        proxy.setTargetFilterLifecycle(true);
	        proxy.setTargetBeanName("shiroFilter");
	        filterRegistrationBean.setFilter(proxy);
	        return filterRegistrationBean;
	    }
	}

自定义 UsernamePasswordToken

	/**
	 * 用户和密码(包含验证码)令牌类
	 */
		public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken {
		private static final long serialVersionUID = 1L;
		private String captcha;
		private boolean mobileLogin;	
		private String sessionid;
		public UsernamePasswordToken() {
			super();
		}
		public UsernamePasswordToken(String username, char[] password,
				boolean rememberMe, String host, String captcha, boolean mobileLogin) {
			super(username, password, rememberMe, host);
			this.captcha = captcha;
			this.mobileLogin = mobileLogin;
		}
		public UsernamePasswordToken(String username,String password, String sessionid){	
			super(username,password);
			this.sessionid = sessionid;
		}
		public String getCaptcha() {
			return captcha;
		}
		public void setCaptcha(String captcha) {
			this.captcha = captcha;
		}
		public String getSessionid() {
			return sessionid;
		}
		public void setSessionid(String sessionid) {
			this.sessionid = sessionid;
		}
		public boolean isMobileLogin() {
			return mobileLogin;
		}
	}

自定义 FormAuthenticationFilter

	import javax.servlet.ServletRequest;
	import javax.servlet.ServletResponse;
	import javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
	import org.apache.shiro.authc.AuthenticationToken;
	import org.apache.shiro.web.util.WebUtils;
	import org.springframework.stereotype.Service;
	import com.alibaba.fastjson.JSON;
	import rongheng.member.entity.ResponseState;
	import rongheng.member.utils.StringUtils;
	/**
	 * 表单验证(包含验证码)过滤类
	 * @author ThinkGem
	 * @version 2014-5-19
	 */
	@Service
	public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
	
		public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
		public static final String DEFAULT_MOBILE_PARAM = "mobileLogin";
		public static final String DEFAULT_MESSAGE_PARAM = "message";
	
		private String captchaParam = DEFAULT_CAPTCHA_PARAM;
		private String mobileLoginParam = DEFAULT_MOBILE_PARAM;
		private String messageParam = DEFAULT_MESSAGE_PARAM;
		
		protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
			String username = getUsername(request);
			String password = getPassword(request);
			if (password==null){
				password = "";
			}
			boolean rememberMe = isRememberMe(request);
			String host = StringUtils.getRemoteAddr((HttpServletRequest)request);
			String captcha = getCaptcha(request);
			boolean mobile = isMobileLogin(request);
			return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha, mobile);
		}
		@Override
	    public boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
			if (this.isLoginRequest(request, response)) {
	    		//createToken(request,response);  这个写在了登录里面
	            return true;
	        } else {
	        //实现CORS跨域配置
	        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
			httpServletResponse.setContentType("application/json;charset=UTF-8");
			httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
			httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
			httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin,Content-Type,Accept,Authentication-Token,X-Requested-With");
	         ResponseState responseState = new ResponseState();
			 	 responseState.setState("403");
			 	 responseState.setMsg("用户未登录");
			 	 httpServletResponse.getWriter().write(JSON.toJSONString(responseState));	        	
	        	 return false;
	       }
	    }
		public String getCaptchaParam() {
			return captchaParam;
		}
		protected String getCaptcha(ServletRequest request) {
			return WebUtils.getCleanParam(request, getCaptchaParam());
		}
		public String getMobileLoginParam() {
			return mobileLoginParam;
		}	
		protected boolean isMobileLogin(ServletRequest request) {
	        return WebUtils.isTrue(request, getMobileLoginParam());
	    }	
		public String getMessageParam() {
			return messageParam;
		}
	}

自定义ShiroRealm

	import org.apache.commons.codec.DecoderException;
	import org.apache.commons.codec.binary.Hex;
	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.authc.credential.HashedCredentialsMatcher;
	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 rongheng.member.entity.SysUser;
	import rongheng.member.service.pc.UserService;
	import javax.annotation.PostConstruct;
	import java.util.ArrayList;
	import java.util.List;
	
	public class ShiroRealm extends AuthorizingRealm {
	
	    @Autowired
	    @Lazy
	    private UserService userService;

	    @Override
	    public String getName() {
	        return "ShiroRealm";
	    }
    //权限认证
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取登录用户
        SysUser sysUser = (SysUser) SecurityUtils.getSubject().getPrincipal();
        List<String> permissions = new ArrayList<String>();
        List<String> roles = new ArrayList<>();
       //授予所有权限
        if (sysUser.getUsername().equals("admin")){
            permissions.add("*:*");
            roles = userService.getRoles(sysUser.getUserid());
        }else {
      		//查询当前用户所拥有的所有权限
           permissions = userService.getPermission(sysUser.getUserid());
           //查询当前用户所拥有的所有角色
           roles = userService.getRoles(sysUser.getUserid());
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(permissions);
        info.addRoles(roles);
        return info;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //通过username 和sessionid 登录
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username=(String) token.getUsername();
        SysUser sysUser = userService.getUserByUsername(username);
        if (sysUser == null){
            return  null;
        }
        //getName() 此处为获取的本类名
        byte[] salt = decodeHex(sysUser.getPassword().substring(0,16));
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(sysUser,sysUser.getPassword().substring(16),
                ByteSource.Util.bytes(salt),getName());
        return simpleAuthenticationInfo;
    }
    /**
     * 设定密码校验的Hash算法与迭代次数   @PostConstruct 密码加密规则   在项目加载的时候执行
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher("SHA-1");
        matcher.setHashIterations(1024);
        setCredentialsMatcher(matcher);
    }
    /**
     * Hex解码.
     * @throws Exception
     */
    public static byte[] decodeHex(String input){
        try {
            return Hex.decodeHex(input.toCharArray());
        } catch (DecoderException e) {
            throw new RuntimeException(e);
        }
      }
  	}

权限测试

权限表

权限表(测试)
角色表
在这里插入图片描述

		 /**
		 * 修改公司员工信息,必须同时拥有admin角色和user:updateSysUser权限才能通过认证
		 * @param sysUser
		 * @return
		 */
		@RequiresRoles(value = "admin")
		@RequiresPermissions("user:updateSysUser")
	    @RequestMapping("updateSysUser")
		public ResponseState updateSysUser(SysUser sysUser){
			 ResponseState responseState = userService.updateSysUser(sysUser);
			 return responseState;
		 }	

捕获权限异常

	import javax.servlet.http.HttpServletRequest;
	import org.apache.shiro.authz.UnauthorizedException;
	import org.springframework.web.bind.annotation.ControllerAdvice;
	import org.springframework.web.bind.annotation.ExceptionHandler;
	import org.springframework.web.bind.annotation.ResponseBody;
	import rongheng.member.entity.ResponseState;
	
	@ControllerAdvice
	public class GlobalDefaultExceptionHandler{
		/**
		 * @RequiresPermissions("user:updateSysUser") 捕获无权限异常
		 * @param req
		 * @param e
		 * @return
		 */
		 @ExceptionHandler(UnauthorizedException.class)
	     @ResponseBody
	     public Object defaultExceptionHandler(HttpServletRequest req,Exception e){
			 ResponseState responseState = new ResponseState();
			 responseState.setState("-200");
			 responseState.setMsg("对不起,暂无权限!");
			return responseState;
	     }	     
	}

自定义返回信息 ResponseState

	import com.fasterxml.jackson.annotation.JsonInclude;
	@JsonInclude(JsonInclude.Include.NON_NULL)
	public class ResponseState {
		private String state="0";
		private String msg;	
		private String sessionid;
		private String token;
		private SysUser sysUser;
		private Company company;
		public SysUser getSysUser() {
			return sysUser;
		}
		public void setSysUser(SysUser sysUser) {
			this.sysUser = sysUser;
		}
		public String getState() {
			return state;
		}
		public void setState(String state) {
			this.state = state;
		}
		public String getMsg() {
			return msg;
		}
		public void setMsg(String msg) {
			this.msg = msg;
		}
		public String getSessionid() {
			return sessionid;
		}
		public void setSessionid(String sessionid) {
			this.sessionid = sessionid;
		}
		public String getToken() {
			return token;
		}
		public void setToken(String token) {
			this.token = token;
		}
		public Company getCompany() {
			return company;
		}
		public void setCompany(Company company) {
			this.company = company;
		}
		}

自定义StringUtils

public class StringUtils {
 	/**
     * 获得用户远程地址
     */
    public static String getRemoteAddr(HttpServletRequest request) {
        String remoteAddr = request.getHeader("X-Real-IP");
        if (isNotBlank(remoteAddr)) {
            remoteAddr = request.getHeader("X-Forwarded-For");
        } else if (isNotBlank(remoteAddr)) {
            remoteAddr = request.getHeader("Proxy-Client-IP");
        } else if (isNotBlank(remoteAddr)) {
            remoteAddr = request.getHeader("WL-Proxy-Client-IP");
        }
        return remoteAddr != null ? remoteAddr : request.getRemoteAddr();
    }
}

自定义TokenUtil

package rongheng.sales.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.StringUtils;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class TokenUtil {

	  /**
     * APP登录Token的生成和解析
     * 
     */
    /** token秘钥,请勿泄露,请勿随便修改 backups:JKKLJOoasdlfj */
    public static final String SECRET = "123";
    /** token 过期时间: 10天 */
    public static final int calendarField = Calendar.DATE;
    public static final int calendarInterval = 10;
    
    /**
     * JWT生成Token.
* * JWT构成: header, payload, signature * * @param phone * 登录成功后用户user_id, 参数user_id不可传空 */
public static String createToken(String phone){ try { Date iatDate = new Date(); // expire time /*Calendar nowTime = Calendar.getInstance(); nowTime.add(calendarField, calendarInterval); Date expiresDate = nowTime.getTime();*/ // header Map Map<String, Object> map = new HashMap<>(2); map.put("alg", "HS256"); map.put("typ", "JWT"); // build token // param backups {iss:Service, aud:APP} String token = JWT.create().withHeader(map) // header .withClaim("iss", "Service") // payload .withClaim("aud", "APP").withClaim("phone", null == phone ? null : phone) .withIssuedAt(iatDate) // sign time //.withExpiresAt(expiresDate) // expire time .sign(Algorithm.HMAC256(SECRET)); // signature return token; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 解密Token * * @param token * @return * @throws Exception */ public static Map<String, Claim> verifyToken(String token) { DecodedJWT jwt = null; try { JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build(); jwt = verifier.verify(token); } catch (Exception e) { e.printStackTrace(); // token 校验失败, 抛出Token验证非法异常 } return jwt.getClaims(); } /** * 根据Token获取userid * * @param token * @return user_id */ public static String getAppUID(String token) { Map<String, Claim> claims = verifyToken(token); Claim user_id_claim = claims.get("phone"); if (null == user_id_claim || StringUtils.isEmpty(user_id_claim.asString())) { // token 校验失败, 抛出Token验证非法异常 } return String.valueOf(user_id_claim.asString()); } }

实现登录注册

	import org.apache.commons.codec.binary.Hex;
	import org.apache.commons.lang3.StringEscapeUtils;
	import org.apache.commons.lang3.Validate;
	import org.apache.shiro.SecurityUtils;
	import org.apache.shiro.authc.AuthenticationException;
	import org.apache.shiro.subject.Subject;
	import org.springframework.beans.factory.annotation.Autowired;
	import org.springframework.web.bind.annotation.CrossOrigin;
	import org.springframework.web.bind.annotation.RequestMapping;
	import org.springframework.web.bind.annotation.RequestMethod;
	import org.springframework.web.bind.annotation.RestController;
	import rongheng.member.entity.Company;
	import rongheng.member.entity.ResponseState;
	import rongheng.member.entity.SysUser;
	import rongheng.member.realm.UsernamePasswordToken;
	import rongheng.member.service.pc.UserService;
	import rongheng.member.utils.IdGen;
	import rongheng.member.utils.StringUtils;
	import rongheng.member.utils.TokenUtil;
	
	import javax.servlet.http.HttpServletRequest;
	import javax.servlet.http.HttpServletResponse;
	import javax.servlet.http.HttpSession;
	import java.security.GeneralSecurityException;
	import java.security.MessageDigest;
	import java.security.SecureRandom;
	
	@CrossOrigin
	@RestController
	@RequestMapping(value = "main",method = RequestMethod.POST)
	public class MainAction {
	
	  @Autowired
	  private UserService userService;

    @RequestMapping("login")
    public ResponseState login(String username, String password, String rememberMe, HttpSession session,
                               HttpServletRequest request, HttpServletResponse response){
        Subject subject = SecurityUtils.getSubject();
         //设置session过期时间为永久
        SecurityUtils.getSubject().getSession().setTimeout(-1);
        if (password == null){
            password = "";
        }
        String host = StringUtils.getRemoteAddr((HttpServletRequest) request);
        UsernamePasswordToken token = new UsernamePasswordToken(username, password.toCharArray(), false, host, null,
                false);
        ResponseState responseState = new ResponseState();
        try {
            subject.login(token);
        }catch (AuthenticationException e){
            responseState.setMsg("用户名或者密码错误");
            e.printStackTrace();
            return responseState;
        }
        if (subject.isAuthenticated()){
            session.setAttribute("user",subject.getPrincipal());
            responseState.setSessionid(subject.getSession().getId().toString());//sessionid
            SysUser user = userService.getUserByUsername(username);
            responseState.setToken(TokenUtil.createToken(user.getPhone()));
            responseState.setSysUser(user);
            responseState.setState("200");
            responseState.setMsg("登录成功");
            return responseState;
        }else {
            responseState.setMsg("登录失败");
            return responseState;
        }
    }
    /**
     * 用户注册
     * @param sysUser
     * @return
     */
    @RequestMapping(value="userRegister")
    public ResponseState userRegister(SysUser sysUser){
        ResponseState responseState = new ResponseState();
        SysUser user = userService.getUserByUsername(sysUser.getUsername());
        if (user != null){
            responseState.setState("405");
            responseState.setMsg("账户已存在");
            return responseState;
        }
        //验证公司是否存在
        Company company = userService.verifyCompany(sysUser.getAuthorizationCode(),sysUser.getCompanyPassword());
        responseState.setCompany(company);
        if (company != null){
            SysUser su = new SysUser();
            su.setUserid(IdGen.uuid());
            su.setPassword(entryptPassword(sysUser.getPassword()));
            int i = userService.userRegister(su);
            if (i == 1){
                responseState.setSysUser(su);
                responseState.setState("200");
                responseState.setMsg("注册成功");
            }
        }else {
            responseState.setState("405");
            responseState.setMsg("授权码或密码错误");
        }
        return responseState;
    }
    /**
     * 生成安全的密码,生成随机的16位salt并经过1024次 sha-1 hash
     */
    public String entryptPassword(String plainPassword) {
        String plain = unescapeHtml(plainPassword);
        byte[] salt = generateSalt(8);
        byte[] hashPassword = sha1(plain.getBytes(), salt, 1024);
        return encodeHex(salt)+encodeHex(hashPassword);
    }
    /**
     * Hex编码.
     */
    public static String encodeHex(byte[] input) {
        return new String(Hex.encodeHex(input));
    }
    /**
     * Html 解码.
     */
    public String unescapeHtml(String htmlEscaped) {
        return StringEscapeUtils.unescapeHtml4(htmlEscaped);
    }
    /**
     * 生成随机的Byte[]作为salt.
     *
     * @param numBytes byte数组的大小
     */
    public byte[] generateSalt(int numBytes) {
        SecureRandom random = new SecureRandom();
        Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", numBytes);
        byte[] bytes = new byte[numBytes];
        random.nextBytes(bytes);
        return bytes;
    }
    public byte[] sha1(byte[] input, byte[] salt, int iterations) {
        return digest(input,"SHA-1", salt, iterations);
    }
    /**
     * 对字符串进行散列, 支持md5与sha1算法.
     */
    private byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) {
        try {
            MessageDigest digest = MessageDigest.getInstance(algorithm);

            if (salt != null) {
                digest.update(salt);
            }
            byte[] result = digest.digest(input);
            for (int i = 1; i < iterations; i++) {
                digest.reset();
                result = digest.digest(result);
            }
            return result;
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }
   	 }
	}

你可能感兴趣的:(Java,shiro,springboot)