spring boot shiro整合

来源:互联网 发布:603881数据港股票 编辑:程序博客网 时间:2024/05/21 19:34

spring boot 整合shiro需要的jar

<properties><shiro.version>1.4.0</shiro.version></properties><dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-guice</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-quartz</artifactId><version>${shiro.version}</version></dependency></dependencies>

shiro配置类:

package com.zyc.springboot.config;import java.util.ArrayList;import java.util.Collection;import java.util.LinkedHashMap;import java.util.Map;import javax.servlet.Filter;import org.apache.shiro.authc.credential.HashedCredentialsMatcher;import org.apache.shiro.cache.ehcache.EhCacheManager;import org.apache.shiro.session.SessionListener;import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;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.CookieRememberMeManager;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.apache.shiro.web.servlet.SimpleCookie;import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;import org.springframework.beans.factory.annotation.Qualifier;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.data.redis.core.RedisTemplate;import org.springframework.web.filter.DelegatingFilterProxy;import com.zyc.springboot.shiro.MyFormAuthenticationFilter;import com.zyc.springboot.shiro.MyRealm;@Configurationpublic class ShiroConfig {@Bean(name = "shiroEhcacheManager")public EhCacheManager getEhCacheManager() {EhCacheManager em = new EhCacheManager();em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");return em;}@Bean(name = "lifecycleBeanPostProcessor")public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {LifecycleBeanPostProcessor lifecycleBeanPostProcessor = new LifecycleBeanPostProcessor();return lifecycleBeanPostProcessor;}@Bean(name = "sessionValidationScheduler")public ExecutorServiceSessionValidationScheduler getExecutorServiceSessionValidationScheduler() {ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler();scheduler.setInterval(900000);//scheduler.setSessionManager(defaultWebSessionManager());return scheduler;}@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;hashedCredentialsMatcher.setHashIterations(1);// 散列的次数,比如散列两次,相当于md5(md5(""));return hashedCredentialsMatcher;}@Bean(name = "defaultWebSecurityManager")public DefaultWebSecurityManager defaultWebSecurityManager(MyRealm myRealm,DefaultWebSessionManager defaultWebSessionManager) {DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();defaultWebSecurityManager.setRealm(myRealm);defaultWebSecurityManager.setCacheManager(getEhCacheManager());defaultWebSecurityManager.setSessionManager(defaultWebSessionManager);defaultWebSecurityManager.setRememberMeManager(rememberMeManager());return defaultWebSecurityManager;}@Bean(name = "rememberMeCookie")public SimpleCookie rememberMeCookie() {// 这个参数是cookie的名称,对应前端的checkbox的name = rememberMeSimpleCookie simpleCookie = new SimpleCookie("rememberMe");// <!-- 记住我cookie生效时间30天 ,单位秒;-->simpleCookie.setMaxAge(259200);return simpleCookie;}/** * cookie管理对象; *  * @return */@Bean(name = "rememberMeManager")public CookieRememberMeManager rememberMeManager() {CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();cookieRememberMeManager.setCookie(rememberMeCookie());return cookieRememberMeManager;}@Bean@DependsOn(value = "lifecycleBeanPostProcessor")public MyRealm myRealm() {MyRealm myRealm = new MyRealm();myRealm.setCacheManager(getEhCacheManager());myRealm.setCredentialsMatcher(hashedCredentialsMatcher());return myRealm;}@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator getAutoProxyCreator(){DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();creator.setProxyTargetClass(true);return creator;}@Beanpublic AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager) {AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();aasa.setSecurityManager(defaultWebSecurityManager);return aasa;}@Bean(name = "sessionManager")public DefaultWebSessionManager defaultWebSessionManager(SessionDao sessionDao) {DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setGlobalSessionTimeout(18000000);////url中是否显示session IdsessionManager.setSessionIdUrlRewritingEnabled(false);//// 删除失效的sessionsessionManager.setDeleteInvalidSessions(true);sessionManager.setSessionValidationSchedulerEnabled(true);sessionManager.setSessionValidationInterval(18000000);sessionManager.setSessionValidationScheduler(getExecutorServiceSessionValidationScheduler());//设置SessionIdCookie 导致认证不成功,不从新设置新的cookie,从sessionManager获取sessionIdCookie//sessionManager.setSessionIdCookie(simpleIdCookie());sessionManager.getSessionIdCookie().setName("session-z-id");sessionManager.getSessionIdCookie().setPath("/");sessionManager.getSessionIdCookie().setMaxAge(60*60*24*7);return sessionManager;}@Bean(name = "filterRegistrationBean1")public FilterRegistrationBean filterRegistrationBean() {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();filterRegistrationBean.setFilter(new DelegatingFilterProxy("shiroFilter"));filterRegistrationBean.addInitParameter("targetFilterLifecycle", "true");filterRegistrationBean.setEnabled(true);filterRegistrationBean.addUrlPatterns("/");return filterRegistrationBean;}@Bean(name = "shiroFilter")public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {// SecurityUtils.setSecurityManager(defaultWebSecurityManager);ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setLoginUrl("/login");shiroFilterFactoryBean.setSuccessUrl("/getMyJsp");shiroFilterFactoryBean.setUnauthorizedUrl("/login");shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);Map<String, Filter> filterMap1 = shiroFilterFactoryBean.getFilters();//自定义的filter 不能交给spring 容器管理,只能使用new 实例化filterfilterMap1.put("authc", new MyFormAuthenticationFilter());shiroFilterFactoryBean.setFilters(filterMap1);Map<String, String> filterMap = new LinkedHashMap<String, String>();filterMap.put("/static/**", "anon");filterMap.put("/logout", "logout");                filterMap.put("/login", "authc");filterMap.put("/**", "authc");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);return shiroFilterFactoryBean;}}
MyRealm类如下:

package com.zyc.springboot.shiro;import java.lang.reflect.InvocationTargetException;import org.apache.commons.beanutils.BeanUtils;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.realm.AuthorizingRealm;import org.apache.shiro.realm.Realm;import org.apache.shiro.subject.PrincipalCollection;import com.zyc.springboot.entity.User;public class MyRealm extends AuthorizingRealm {/** * 权限认证 */@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// TODO Auto-generated method stub//获取登录用户的信息,在认证时存储的是ShiroUser 所以得到的就是ShiroUser//在其他地方也可通过SecurityUtils.getSubject().getPrincipals()获取用户信息ShiroUser sysUser =  (ShiroUser) principals.getPrimaryPrincipal();//权限字符串List<String> permissions=new ArrayList<>();//从数据库中获取对应权限字符串并存储permissions//角色字符串List<String> roles=new ArrayList<>();//从数据库中获取对应角色字符串并存储rolesSimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();simpleAuthorizationInfo.addStringPermissions(permissions);simpleAuthorizationInfo.addRoles(roles);//角色类型return simpleAuthorizationInfo;}/** * 登录验证 */@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {System.out.println("认证=====");String userName=((MyAuthenticationToken) arg0).getUsername();char[] password=((MyAuthenticationToken) arg0).getPassword();User user=new User();//根据用户名密码获取user,这里不在连接数据库user.setPassword("e10adc3949ba59abbe56e057f20f883e");//123456 md5加密后的值user.setUserName("zyc");//user信息 本该从数据库中获取,这里为了简单,直接模拟if(user==null){throw new AuthenticationException("用户名密码错误");}ShiroUser shiroUser=new ShiroUser();//自定义的用户信息类,在shiro中存储使用try {BeanUtils.copyProperties(shiroUser, user);//user信息赋给shiroUser} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(shiroUser, user.getPassword(), this.getName());return simpleAuthenticationInfo;}}
自定义filter  MyFormAuthenticationFilter如下

package com.zyc.springboot.shiro;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.annotation.WebFilter;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.IncorrectCredentialsException;import org.apache.shiro.authc.LockedAccountException;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.subject.Subject;import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;import org.apache.shiro.web.util.WebUtils;import org.springframework.core.annotation.Order;public class MyFormAuthenticationFilter extends FormAuthenticationFilter {// 登录失败,异常抛出@Overrideprotected boolean onLoginFailure(AuthenticationToken token,AuthenticationException e, ServletRequest request,ServletResponse response) {String className = e.getClass().getName();if (e != null&& !UnknownAccountException.class.getName().equals(className)&& !IncorrectCredentialsException.class.getName().equals(className)&& !LockedAccountException.class.getName().equals(className)) { // 用户被锁定e.printStackTrace(); // 非验证异常抛出}return super.onLoginFailure(token, e, request, response);}// 重写认证通过后的页面跳转,shiro会默认跳转到上一次请求的页面,不适用于iframe的框架@Overrideprotected void issueSuccessRedirect(ServletRequest request,ServletResponse response) throws Exception {// 认证通过后的跳转地址System.out.println("认证通过后的跳转地址"+getSuccessUrl());WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true);}@Overrideprotected AuthenticationToken createToken(ServletRequest request,ServletResponse response) {System.out.println("create Token");String username = getUsername(request);String password = getPassword(request);boolean remberMe = isRememberMe(request);String host = "";String captcha = "";String ipAddr = "";return new MyAuthenticationToken(username, password, remberMe, host,captcha, ipAddr);}}
自定义token类如下:

package com.zyc.springboot.shiro;import org.apache.shiro.authc.UsernamePasswordToken;/** * 用来存储验证码等其他信息 * @author Administrator * */public class MyAuthenticationToken extends  UsernamePasswordToken {/** *  */private static final long serialVersionUID = -2681757716434500100L;private String captcha;private String ipAddr;public MyAuthenticationToken(String username,String password,boolean rememberMe,String host,String captcha,String ipAddr) {        super(username, password, rememberMe, host);    this.captcha = captcha;    this.ipAddr=ipAddr;}public String getCaptcha() {return captcha;}public void setCaptcha(String captcha) {this.captcha = captcha;}public String getIpAddr() {return ipAddr;}public void setIpAddr(String ipAddr) {this.ipAddr = ipAddr;}}
shiro登录认证调用有2种认证方式,个人理解为主动触发和被动触发,

主动触发是通过调用subject.login(token)方法如下:这种方式shiro 的/login=anon

@RequestMapping("tologin")public String login(Model model, HttpServletRequest request,HttpServletResponse response) throws Exception {System.out.println("login =======start==");Subject subject = SecurityUtils.getSubject();subject.login(token);System.out.println("login ======end===");return "login";}
被动触发:是在shiro 的loginurl属性和登录表单的action跳转url 一致,get请求直接返回界面,post请求会调用认证 这种方式shiro 的/login=authc

@RequestMapping("login")public String login(Model model, HttpServletRequest request,HttpServletResponse response) throws Exception {System.out.println("login =======start==");Subject subject = SecurityUtils.getSubject();if (subject.isAuthenticated()) { // 已经登录,跳转到自己的首页WebUtils.issueRedirect(request, response, "/getMyJsp");return "login";}System.out.println("login ======end===");return "login";}

问题小结:

shiro 登录后,样式不显示

可能原因,shiro的filter 把样式给拦截了,注意shiro自定义的filter 不能交给spring 管理,并且要注意拦截器的顺序,shiroFilter 在其他拦截器之前

shiro 报there is no session with id []

产生此原因可能是在代码中的某个地方调用了session.invalidate(),或者shiro 的logout方法,之后又使用了request,session的方法

shiro 点击登录,或者认证成功后继续点击,总是返回到登录页面

正常情况下如果认证失败会返回登录界面,如果认证通过了返回登录界面可以查看设置sessionManager 的setSessionIdCookie()是如何设置cookie的,尽量不要创建新的cookie 而是使用sessionManager.getSessionIdCookie()获取默认的cookie进行设置