spring集成shiro实现权限认证和自动登录
在登录入口类中先创建用户名/密码身份验证Token。
UsernamePasswordToken token = new UsernamePasswordToken(phone, password);
然后调用subject.login(token);此时SecurityManager将会委托Authenticator进行身份验证,Authenticator会将token传入Realm从Realm获取身份验证信息,如果没有返回或抛出异常表示身份验证失败。用户登录成功可以将用户身份信息放入shiroSession中,在自定义的sessionExpiredFilter类中通过判断请求的session中是否存在用户信息,处理过期的登录请求。(浏览器每次请求会将sessionId通过request传给服务端,通过sessionId获取当前用户的session信息)
Subject subject = SecurityUtils.getSubject(); User user = userService.getUserByPhone(phone); //创建用户名、密码身份验证Token UsernamePasswordToken token = new UsernamePasswordToken(phone, password); String loginToken = new Md5Hash(new Date().toString() + ShiroSession.getId()).toHex(); try { subject.login(token); ShiroSession.set("user_info", JSON.toJSONString(user)); //如果登录成功且设置了7天自动登录 if (remember == true) { userService.updateToken(user.getPhone(), loginToken); //设置客户端cookie保存7天 CookieUtil.addCookie(response, "loginToken", loginToken, 7); } return ResponseUtils.buildOKResponse(JSON.toJSONString(new User(user.getId(), user.getNickName())));
UserRealm用户权限认证:
UserRealm继承AuthorizingRealm类,实现两个方法doGetAuthenticationInfo和doGetAuthorizationInfo
在doGetAuthenticationInfo中从通过
String phone = (String) token.getPrincipal();获得角色信息
String password = new String((char[]) token.getCredentials());获得登录密码
然后与从数据库中查询出的用户密码进行匹配,进行判断逻辑处理,无误后将用户的登录信息存入SimpleAuthenticationInfo并返回:
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo( phone, password, ByteSource.Util.bytes(phone),//设置username、pwd都没问题 getName() //realm name(唯一) ); return info;
在doGetAuthorizationInfo类中主要实现对已认证的用户分配资源权限:
String phone = (String) principalCollection.getPrimaryPrincipal();获得认证用户,然后通过自定义的getResourcesByUserId方法获取用户资源列表,放入HashSet中,最后调用SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(new HashSet<>(permissions));将集合内容填充到认证中
ResourceCheckFilter访问路径的认证
ResourceCheckFilter继承AccessControlFilter实现两个方法isAccessAllowed和onAccessDenied,负责访问路径的认证判断:
@Override protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception { Subject subject = getSubject(servletRequest, servletResponse); String url = getPathWithinApplication(servletRequest); return subject.isPermitted(url); }
isAccessAllowed方法通过获取request的请求路径,调用isPermitted方法,判断该资源是否可用,如果返回true,走到下一个过滤器。如果没有下一个过滤器的话,表示具有了访问某个资源的权限,如果返回 false,则会调用 onAccessDenied 方法,去实现相应的当过滤不通过的时候执行的操作。
onAccessDenied方法对没有访问权限的页面做处理:
@Override @ResponseBody protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { HttpServletResponse response = (HttpServletResponse) servletResponse; HttpServletRequest request = (HttpServletRequest) servletRequest; if (WebUtil.isAjax(request)) { //返回403 FORBIDDEN状态码 response.setStatus(403); } //如果是普通请求,进行页面重定向 else { response.sendRedirect(request.getContextPath()+"/403"); } return false; }
UrlPermissionResolver解析权限字符串
实现PermissionResolver接口,它负责解析权限字符串到Permission实例,RolePermissionResolver用于根据角色解析相应的权限集合。
WildcardPermission是shiro默认的权限字符串的表示方式,此处根据我们自己的url格式自定义UrlPermission类进行权限匹配。
UrlPermission 是一个实现了Permission接口的类,它的implies方法的实现决定了权限是否匹配。
public class UrlPermissionResolver implements PermissionResolver { @Override public Permission resolvePermission(String s) { if (s.startsWith("/")) { return new UrlPermission(s); } else { return new WildcardPermission(s); } } } class UrlPermission implements Permission { private String url; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public UrlPermission() { } public UrlPermission(String url) { this.url = url; } @Override public boolean implies(Permission permission) { if (!(permission instanceof UrlPermission)) { return false; } UrlPermission urlPermission = (UrlPermission) permission; PatternMatcher patternMatcher = new AntPathMatcher(); return patternMatcher.matches(this.getUrl(), urlPermission.getUrl()); } }
spring-shiro.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--自定义权限字符串解析规则,用/替换:--> <bean id="urlPermissionResolver" class="com.legolas.blog.config.shiroconfig.UrlPermissionResolver"/> <!--用户及权限Realm实现--> <bean id="permissionRealm" class="com.legolas.blog.config.shiroconfig.UserRealm"></bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="permissionRealm"/> <property name="sessionManager" ref="sessionManager"/> <property name="cacheManager" ref="ehCacheManager"/> <property name="authorizer.permissionResolver" ref="urlPermissionResolver"/> </bean> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- 设置超时时间 --> <property name="globalSessionTimeout" value="6048000"/> <property name="deleteInvalidSessions" value="true"/> <property name="sessionValidationSchedulerEnabled" value="true"/> <property name="sessionIdCookieEnabled" value="true"/> <property name="sessionIdCookie" ref="sessionIdCookie"/> </bean> <!--shiroFilter--> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="loginUrl" value="/index"/> <property name="securityManager" ref="securityManager"/> <property name="successUrl" value="/index"/> <property name="filterChainDefinitions"> <!--默认拦截器: authc:基于表单的拦截器,没有登陆会跳到相应的登录页面登录如 /**=authc logout:退出拦截器如/logout=logout anon:匿名拦截器,即不需要登录就可访问 如/static/**=anon authc是认证过,user是登录过,如果开启了rememberMe功能的话,后者(user)也是可以通过的, 而前者(authc)通过不了。故我们用authc来校验一些关键操作,比如购买,我们可以采用user校验即可。 而支付的时候,我们需要认证的用户,那就需要authc了。 --> <value> /assets/** = anon /admin/**= sessionExpiredFilter,user,resourceFilter <!--/archive = sessionExpiredFilter 归档中需区分登录超时以显示锁定文章///该设计不合理,还是将锁定与未锁定文章放两个入口--> <!-- /admin/**= authc,resourceFilter--> </value> </property> </bean> <!--角色过滤器--> <bean id="resourceFilter" class="com.legolas.blog.config.shiroconfig.ResourceCheckFilter"/> <bean id="sessionExpiredFilter" class="com.legolas.blog.config.shiroconfig.SessionExpiredFilter"/> <!--info的加密算法--> <bean id="hashMatch" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="md5"/> </bean> <!--缓存管理--> <bean id="ehCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache/shiro-ehcache.xml"/> </bean> </beans>
可以看到上面spring-shiro的shiroFilter拦截器中还定义了一个sessionExpiredFilter,在该过滤器中我们实现自己的自动登录逻辑和登录过期请求的处理:
首先在用户登录认证代码中如果用户登录成功,且选择了自动登录选项,我们保存用户的登录状态信息到session中,然后生成一个loginToken认证码,更新到user表的字段中:
String loginToken = new Md5Hash(new Date().toString() + ShiroSession.getId()).toHex(); try { subject.login(token); ShiroSession.set("user_info", JSON.toJSONString(user)); //如果登录成功且设置了7天自动登录 if (remember == true) { userService.updateToken(user.getPhone(), loginToken); //设置客户端cookie保存7天 CookieUtil.addCookie(response, "loginToken", loginToken, 7); } return ResponseUtils.buildOKResponse(JSON.toJSONString(new User(user.getId(), user.getNickName()))); }catch(){ }
SessionExpiredFilter过滤器
在自定义的SessionExpiredFilter过滤器中写http请求的前置拦截逻辑代码,先判断session中用户的登录状态,若不存在则用户未登录,然后判断request请求中是否存在loginToken(未选择自动登录或者已过7天token失效),若存在,通过该token值从数据库中查询与之匹配的用户,若存在,则执行登录代码,若不存在,则执行逻辑跳转处理:
public class SessionExpiredFilter extends PathMatchingFilter { @Autowired UserService userService; @Override protected boolean onPreHandle(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) throws Exception { HttpServletResponse response = (HttpServletResponse) servletResponse; HttpServletRequest request = (HttpServletRequest) servletRequest; if (ShiroSession.get("user_info") != null) { return true; } else { //判断请求端带过来cookie是否存在 String loginToken = CookieUtil.findCookieByName(request, "loginToken"); if (StringUtils.isNotBlank(loginToken)) { //到数据库查询有没有该Cookie User user = userService.findUserByToken(loginToken); if (user != null) { try { //重新登录 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(user.getPhone(), user.getPassword()); subject.login(token); ShiroSession.set("user_info", JSON.toJSONString(user)); return true; } catch (AuthenticationException e) { e.printStackTrace(); return false; } } else { //没有该Cookie与之对应的用户(Cookie不匹配) CookieUtil.clearCookie(request, response, "loginToken"); return false; } } else { //没有登录,也没有cookie凭证 if (WebUtil.isAjax(request)) { response.setStatus(401); } else { response.sendRedirect(request.getContextPath() + "/401"); } return false; } } } }
在此过程中尝试过使用UserInterceptor,在拦截器中处理登录过期请求,但是shiroFilter过滤器的处理顺序在Spring的拦截器之前执行,处理请求无法到达interceptor。
配置中的注入的shiroFilter在web.xml文件中通过DelegatingFilterProxy对servlet filter的代理,来实现自定义的filter使配置的shiro拦截器生效。
web.xml配置文件
<!--若集成redis管理session时配置该filter放在最前面,使其具有最优先地位接管所有请求的session--> <!-- <filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制
- springmvc+shiro+maven 实现登录认证与权限授权管理 201
- 七、spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制
- springmvc+shiro+maven 实现登录认证与权限授权管理
- springmvc+shiro+maven 实现登录认证与权限授权管理
- springmvc+shiro+maven 实现登录认证与权限授权管理
- springmvc+shiro+maven 实现登录认证与权限授权管理
- shiro 权限认证框集成到spring中,实现登陆与权限拦截
- spring集成shiro实现登录认证自定义验证功能(认证采用国密SM4算法)
- spring-boot(八) springboot整合shiro-登录认证和权限管理
- SpringBoot+shiro整合学习之登录认证和权限控制
- Shiro+easyUI+SpringMVC实现登录认证
- spring boot配置shiro安全框架及用户登录权限验证实现
- 【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限
- springboot整合shiro-登录认证和权限管理
- 单点登录CAS与权限管理框架Shiro集成------Spring项目方式
- shiro实现APP保持登录状态,以及web统一登录认证和权限管理,会话保持在web和APP之间。
- springboot(十四):springboot整合shiro-登录认证和权限管理
- SpringBoot+shiro整合学习之登录认证和权限控制
- shiro实现APP、web统一登录认证和权限管理