Shiro和Spring的集成,涉及到很多相关的配置,涉及到shiro的filer机制以及它拥有的各种默认filter,涉及到shiro的权限判断标签,权限注解,涉及到session管理等等方面。
1. 配置
首先需要在web.xml中专门负责接入shiro的filter:

<!-- shiro 安全过滤器 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
<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>

并且需要放在所有filter中靠前的位置,比如需要放在siteMesh的过滤器之前。
DelegatingFilterProxy 表示这是一个代理filter,它会将实际的工作,交给spring配置文件中 id="shiroFilter" 的bean来处理:

public class DelegatingFilterProxy extends GenericFilterBean {
private String contextAttribute;
private WebApplicationContext webApplicationContext;
private String targetBeanName;
private boolean targetFilterLifecycle = false;
private volatile Filter delegate;
private final Object delegateMonitor = new Object();
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}


public abstract class GenericFilterBean implements
Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean {
@Override
public final void init(FilterConfig filterConfig) throws ServletException {
Assert.notNull(filterConfig, "FilterConfig must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
}
this.filterConfig = filterConfig;
// Set bean properties from init parameters.
try {
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
String msg = "Failed to set bean properties on filter '" +
filterConfig.getFilterName() + "': " + ex.getMessage();
logger.error(msg, ex);
throw new NestedServletException(msg, ex);
}
// Let subclasses do whatever initialization they like.
initFilterBean();
if (logger.isDebugEnabled()) {
logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
}
}

// Let subclasses do whatever initialization they like.
initFilterBean();
Filter 接口的 init 方法调用 initFilterBean(), 而该方法在子类中进行实现,它先获得 this.targetBeanName = getFilterName(); bean的名称,也就是id,然后对其进行初始化:this.delegate = initDelegate(wac); 其实就是从bean工厂中根据bean的名称找到bean.

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}

而 shiroFilter在spring中的配置如下:

<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login"/>
<property name="successUrl" value="/"/>
<property name="unauthorizedUrl" value="/unauthorized"/>
<property name="filters">
<util:map>
<entry key="authc" value-ref="passThruAuthenticationFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/reg/** = anon <!-- 注册相关 -->
/login = authc
/logout = logout
/authenticated = authc
/loginController = anon
/js/** = anon
/css/** = anon
/img/** = anon
/html/** = anon
/font-awesome/** = anon
<!-- /** = anon
/user/modifyPassword = perms["user:update", "user:select"]
-->
/** = user
</value>
</property>
</bean>

上面的shiroFilter的配置又引出了 securityManager 和 shiro 的filter机制和他自带的一些filter.
2. securityManager 级相关配置
在上一篇文章 Java 权限框架 Shiro 实战一:理论基础 中我们知道securityManager是shiro的顶层对象,它管理和调用其它所有子系统,负责系统的安全。我们知道shiro有两个类型的securityManager:一个是JavaSE环境,默认是DefaultSecurityManager;一个是web环境,默认是DefaultWebSecurityManager。所以我们web环境肯定应该使用后者。我们从顶层对象一层一层向下配置。先看securityManager如何配置:
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
上面的配置相当于调用SecurityUtils.setSecurityManager(securityManager) ,来注入了下面配置的 securityManager(DefaultWebSecurityManager) :

<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"/>
<property name="cacheManager" ref="cacheManager"/>
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>

它默认使用的session管理器是 ServletContainerSessionManager,所以上面没有配置,所以就使用默认值。配置了就会覆盖下面的默认值:

public DefaultWebSecurityManager() {
super();
((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
this.sessionMode = HTTP_SESSION_MODE;
setSubjectFactory(new DefaultWebSubjectFactory());
setRememberMeManager(new CookieRememberMeManager());
setSessionManager(new ServletContainerSessionManager());
}

显然 securityManager 最重要的工作就是用户登录认证和获得用户的权限等相关信息,所以 realm 是其最重要的依赖:
<!-- Realm实现 -->
<bean id="userRealm" class="com.ems.shiro.UserRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<property name="cachingEnabled" value="false"/>
</bean>
要理解上面userRealm的配置,就的先理解 UserRealm 的继承体系:

UserRealm 继承 AuthorizingRealm 显然是为了获取权限信息,对用户进行访问控制;继承AuthenticatingRealm显然是为了获得用户的认证信息,对用户进行认证。而 credentialsMatcher 就是 AuthenticatingRealm 使用来进行密码验证的依赖的组件:
public abstract class AuthenticatingRealm extends CachingRealm implements Initializable {/**
* Credentials matcher used to determine if the provided credentials match the credentials stored in the data store.
*/
private CredentialsMatcher credentialsMatcher;
再看其credentialsMatcher bean的配置:

<!-- 凭证匹配器(验证登录密码是否正确) -->
<bean id="credentialsMatcher" class="com.ems.shiro.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager"/>
<property name="hashAlgorithmName" value="SHA-256"/>
<property name="hashIterations" value="2"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>

配置就是 hash加密的相关参数:hash算法,hash迭代次数等。到这里 shiro 登录验证的配置就完了。至于获取用户信息和用户的权限的信息,都在userRealm中实现了:

public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = (String)principals.getPrimaryPrincipal();
User user = userService.getUserByUserName (userName );
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(userService.findRolesByUserId(user.getId()));
authorizationInfo.setStringPermissions(userService.findPermissionsByUserId(user.getId()));
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = (String)token.getPrincipal();
User user = userService.getUserByUserName(userName);
if(user == null) {
throw new UnknownAccountException();//没找到账户
}
if(user.getLocked() == 0) {
throw new LockedAccountException(); //帐号锁定
}
if(user.getLocked() == 2){
throw new AuthenticationException("account was inactive");
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user.getUserName(),
user.getPassword(), // 密码
ByteSource.Util.bytes(user.getCredentialsSalt()), // salt
getName() // realm name
);
return authenticationInfo;
}

securityManager会在需要的时候回调上面 的 doGetAuthorizationInfo 和 doGetAuthenticationInfo 方法,从realm中获得登录认证信息和用户权限信息。至于 rememberMeManager 主要是实现使用cookie表示我已经登录过了,下次不需要重新登录,这一个功能,也就是“记住我”登录过这一功能:

<!-- rememberMe管理器 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)-->
<property name="cipherKey"
value="#{T(org.apache.shiro.codec.Base64).decode('9FvVhtFLUs0KnA3Kprsdyg==')}"/>
<property name="cookie" ref="rememberMeCookie"/>
</bean>
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="2592000"/><!-- 30天 -->
</bean>

还有cacheManager的配置:

<!--ehcache-->
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache/ehcache.xml"/>
</bean>
<bean id="springCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
</bean>
<!-- 缓存管理器 -->
<bean id="cacheManager" class="com.ems.shiro.SpringCacheManagerWrapper">
<property name="cacheManager" ref="springCacheManager"/>
</bean>

使用的是 EhCache.
3. Shiro 的filter机制和自带的filter
Shiro的filter是基于Servlet的Filter接口实现的。我们通过Shiro提供的form登录filter:FormAuthenticationFilter 和 ShiroFilter 看看其实现:

继承中的每一层都实现了一些功能:
1> NameableFilter:实现给filter取名的功能(Allows a filter to be named via JavaBeans-compatible)

/**
* Allows a filter to be named via JavaBeans-compatible*/
public abstract class NameableFilter extends AbstractFilter implements Nameable {
/**
* The name of this filter, unique within an application.
*/
private String name;

2> OncePerRequestFilter : 保证对于同一个request,fiter只执行一次(Filter base class that guarantees to be just executed once per request)
/**
* Filter base class that guarantees to be just executed once per request,
* on any servlet container. It provides a {@link #doFilterInternal}
* method with HttpServletRequest and HttpServletResponse arguments.*/
public abstract class OncePerRequestFilter extends NameableFilter {
3> AdviceFilter: SpringMVC风格的过滤器(就是preHandle, postHandle,afterCompletion 三接口的过滤器)

/**
* A Servlet Filter that enables AOP-style "around" advice for a ServletRequest via
* preHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse),
* postHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse),
* and afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception)hooks.
*/
public abstract class AdviceFilter extends OncePerRequestFilter {
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
return true;
}
@SuppressWarnings({"UnusedDeclaration"})
protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
}
@SuppressWarnings({"UnusedDeclaration"})
public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
}

4> PathMatchingFilter:该过滤器仅仅处理指定的路径(比如上面的配置:/js/** = anon,表示对 /js/ 目录和其子目录的请求,交给anon过滤器处理)
/**
* <p>Base class for Filters that will process only specified paths and allow all others to pass through.</p>*/
public abstract class PathMatchingFilter extends AdviceFilter implements PathConfigProcessor {
5> AccessControlFilter: 实现提供对资源的访问控制,没有权限时,重定向到登录页面,登录之后跳转到原来的那个页面

/**
* Superclass for any filter that controls access to a resource and may redirect the user to the login page
* if they are not authenticated. This superclass provides the method
* saveRequestAndRedirectToLogin(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
* which is used by many subclasses as the behavior when a user is unauthenticated.*/
public abstract class AccessControlFilter extends PathMatchingFilter {

6> AuthenticationFilter: 实现对访问用户的认证要求,也就是必须登录了才能访问
/**
* Base class for all Filters that require the current user to be authenticated. This class encapsulates the
* logic of checking whether a user is already authenticated in the system while subclasses are required to perform
* specific logic for unauthenticated requests.*/
public abstract class AuthenticationFilter extends AccessControlFilter {
7> AuthenticatingFilter: 实现判断用户是否有权限访问某资源。
/**
* An AuthenticationFilter that is capable of automatically performing an authentication attempt
* based on the incoming request.*/
public abstract class AuthenticatingFilter extends AuthenticationFilter {
8> FormAuthenticationFilter:shiro提供的用于实现用户登录功能,如果我们打算自己实现登录,那么我们应用 PassThruAuthenticationFilter 来替代

/**
* Requires the requesting user to be authenticated for the request to continue, and if they are not, forces the user
* to login via by redirecting them to the setLoginUrl(String) you configure.
* If you would prefer to handle the authentication validation and login in your own code, consider using the
* PassThruAuthenticationFilter instead, which allows requests to the loginUrl to pass through to your application's code directly.*/
public class FormAuthenticationFilter extends AuthenticatingFilter {

9> PassThruAuthenticationFilter : 用于我们自己在controller中实现登录逻辑时替代FormAuthenticationFilter

/**
* An authentication filter that redirects the user to the login page when they are trying to access
* a protected resource. However, if the user is trying to access the login page, the filter lets
* the request pass through to the application code.
* The difference between this filter and the FormAuthenticationFilter is that
* on a login submission (by default an HTTP POST to the login URL), the FormAuthenticationFilter filter
* attempts to automatically authenticate the user by passing the username and password request parameter values to
* Subject.login(AuthenticationToken) directly.
* Conversely, this controller always passes all requests to the loginUrl through, both GETs and POSTs.
* This is useful in cases where the developer wants to write their own login behavior, which should include a
* call to Subject.login(AuthenticationToken) at some point. For example, if the developer has their own custom MVC
* login controller or validator, this PassThruAuthenticationFilter may be appropriate.*/
public class PassThruAuthenticationFilter extends AuthenticationFilter {
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if (isLoginRequest(request, response)) {
return true;
} else {
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
}

10> Shiro 自带的filter:
Shiro自身提供了很多的默认filter 来供我们使用,主要分为两种:一是 登录认证相关的filter;一是权限访问控制相关的filter;
登录认证相关的filter有:
1)filter名称: anon, 实现类org.apache.shiro.web.filter.authc.AnonymousFilter,主要用于静态资源的访问,表示无需登录就可以访问;
2)filter名称: authc, 实现类org.apache.shiro.web.filter.authc.FormAuthenticationFilter,主要用于表单登录,没有登录则跳转登录url;
3)filter名称: user, 实现类org.apache.shiro.web.filter.authc.UserFilter,主要用于要求用户已经登录或者通过“记住我”功能登录了也行。
4)filter名称: logout, 实现类org.apache.shiro.web.filter.authc.LogoutFilter,主要用于用户登出
5)filter名称: authcBasic,authc的简化形式,略。
权限访问控制相关的filter有:
1)filter名称: roles, 实现类org.apache.shiro.web.filter.authc.RolesAuthorizationFilter,主要用于验证用户必须拥有某角色,才能继续访问;
2)filter名称: perms, 实现类org.apache.shiro.web.filter.authc.PermissionsAuthorizationFilter,主要用于验证用户必须拥有某权限,才能继续访问;
3)filter名称: ssl, 实现类org.apache.shiro.web.filter.authc.SslFilter,主要用于要求访问协议是https才能访问,不然跳转到https的443短裤;
4)filter名称: port rest noSessionCreation,略。
我们上面的shiroFilter的配置中,已经使用过了上面这些自带的filter:

/reg/** = anon <!-- 注册相关 -->
/login = authc
/logout = logout
/authenticated = authc
/loginController = anon
/js/** = anon
/css/** = anon
/img/** = anon
/html/** = anon
/font-awesome/** = anon
/** = user

我们看到 /reg/** 注册相关的,/js/**静态资源都是使用的 anon匿名过滤器,不要求用户已经登录就可以访问。
/** = user 放在最后是要求除了上面那些 url 之外的访问路径,都需要登录认证过或者通过记住我登录认证过。因为路径比较是从上面开始列出来的先开始比较的,匹配了就走该过滤器,不会继续下面的过滤器了。
4. shiro的权限标签
Shiro提供了相应的权限标签,用来实现根据用户的角色和权限来显示它相应的菜单和按钮。首先需要导入shiro标签库:
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
标签库的定义位于:shiro-web.jar 包中的META-INF/shiro.tld文件中:
shiro.tld
其中最重要的标签是关于角色和权限的:
<shiro:hasAnyRoles name="student,teacher"></shiro:hasAnyRoles>
<shiro:hasPermission name="user:delete"></shiro:hashPermission>
其它还有关于登录与否的标签:
<shiro:guest></shiro:guest> 未登录可以显示的信息;
<shiro:user></shiro:user> 用户已经登录或者通过记住我登录后显示的信息;
<shiro:authenticated></shiro:authenticated> 必须是实际登录,不是通过记住我登录的
其它标签参考 shiro.tld文件。
shiro标签使用示例:
View Code
效果是根据用户拥有的角色,来显示左侧有哪些菜单项。
5. shiro 权限注解的使用
shiro对权限的控制,除了前面给出的在 shiroFilter这个bean中配置的过滤器:

<property name="filterChainDefinitions">
<value>
/reg/** = anon <!-- 注册相关 -->
/login = authc
/logout = logout
/loginController = anon
/js/** = anon
/css/** = anon
/img/** = anon
/html/** = anon
/font-awesome/** = anon
/** = user
</value>
</property>

之外,最重要的就是使用注解的方式来进行访问控制的实现了。shiro权限注解可以达到方法级别的细腻控制,可以控制具有某些权限或者某些角色的用户才能访问某个方法(某个url)。先要开启shiro权限注解功能,开启方法参见文档:http://shiro.apache.org/spring.html
Here is how to enable these annotations. Just add these two bean definitions to applicationContext.xml:

<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after -->
<!-- the lifecycleBeanProcessor has run: -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>

开启shiro权限注解的方法二:
<aop:config />
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<aop:config /> 表示开启spring注解,而 DefaultAdvisorAutoProxyCreator 表示会自动创建代理。但是二者最好不要同时使用。
AuthorizationAttributeSourceAdvisor 通过其依赖的 securityManager 来获取用户的角色和权限信息,进而可以进行权限判断。
支持的shiro注解有:

@SuppressWarnings({"unchecked"})
public class AuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {
private static final Logger log = LoggerFactory.getLogger(AuthorizationAttributeSourceAdvisor.class);
private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
new Class[] {
RequiresPermissions.class, RequiresRoles.class,
RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
};
protected SecurityManager securityManager = null;
public AuthorizationAttributeSourceAdvisor() {
setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor());
}

RequiresPermissions, RequiresRoles, RequiresUser, RequiresGuest, RequiresAuthentication
主要是通过: AopAllianceAnnotationsAuthorizingMethodInterceptor 类来实现的:

public class AopAllianceAnnotationsAuthorizingMethodInterceptor extends AnnotationsAuthorizingMethodInterceptor implements MethodInterceptor{
public AopAllianceAnnotationsAuthorizingMethodInterceptor() {
List<AuthorizingAnnotationMethodInterceptor> interceptors = new ArrayList<AuthorizingAnnotationMethodInterceptor>(5);
//use a Spring-specific Annotation resolver - Spring's AnnotationUtils is nicer than the
//raw JDK resolution process.
AnnotationResolver resolver = new SpringAnnotationResolver();
//we can re-use the same resolver instance - it does not retain state:
interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
interceptors.add(new UserAnnotationMethodInterceptor(resolver));
interceptors.add(new GuestAnnotationMethodInterceptor(resolver));
setMethodInterceptors(interceptors);
}

上面注入了注解的拦截器实现。具体的拦截判断权限过程实现如下:

public class RoleAnnotationHandler extends AuthorizingAnnotationHandler {
public RoleAnnotationHandler() {
super(RequiresRoles.class);
}
public void assertAuthorized(Annotation a) throws AuthorizationException {
if (!(a instanceof RequiresRoles)) return;
RequiresRoles rrAnnotation = (RequiresRoles) a;
String[] roles = rrAnnotation.value();
if (roles.length == 1) {
getSubject().checkRole(roles[0]);
return;
}
if (Logical.AND.equals(rrAnnotation.logical())) {
getSubject().checkRoles(Arrays.asList(roles));
return;
}
if (Logical.OR.equals(rrAnnotation.logical())) {
// Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first
boolean hasAtLeastOneRole = false;
for (String role : roles) if (getSubject().hasRole(role)) hasAtLeastOneRole = true;
// Cause the exception if none of the role match, note that the exception message will be a bit misleading
if (!hasAtLeastOneRole) getSubject().checkRole(roles[0]);
}
}
}

主要是上面的方法 assertAuthorized(Annotation a) 中来实现对用户是否拥有某些角色进行判断的。其实还是很简单的。
shiro权限注解使用方法如下所示:

@RequiresPermissions(value={"user:update", "user:select"}, logical= Logical.AND)
@RequestMapping(value="/modifyPassword", method=RequestMethod.POST)
@ResponseBody
public Map<String, String> modifyPassword(String oldPassword, String newPassword, HttpSession session) {
Map<String, String> map = new HashMap<>();
if(oldPassword == null || newPassword == null || newPassword.length() < 8 || newPassword.length() > 32){
map.put("result", "error");
map.put("msg", "密码必须在8到20位之间");
return map;
}
User user = (User)session.getAttribute(ConstantConfig.LONGIN_USER);
if(user != null){
PasswordHelper ph = new PasswordHelper();
if(!ph.checkPasswordAndEncryptPassword(oldPassword, user)){ // 判断输入的 oldPassword是否正确
map.put("result", "error");
map.put("msg", "密码错误");
return map;
}else{
user.setPassword(newPassword);
ph.encryptPassword(user);
int result = this.userService.updateUserById(user);
if(result > 0){
map.put("result", "ok");
map.put("msg", "密码修改成功,请重新登录");
}else{
map.put("result", "error");
map.put("msg", "密码修改失败");
}
return map;
}
}
return map;
}

@RequiresPermissions(value={"user:update", "user:select"}, logical= Logical.AND)
表示必须有 对 user 表的同时拥有 查询和更新权限,才能修改密码。
分类: Java-shiro
+加关注
4
0
« 上一篇:Java 权限框架 Shiro 实战一:理论基础
» 下一篇:Spring AOP 不同配置方式产生的冲突问题




1131

被折叠的 条评论
为什么被折叠?



