该demo整合Shiro的相关配置参考**开涛的博客**

#数据库表格相关设计
JavaWeb项目:Shiro实现简单的权限控制(整合SSM)

表格设计得比较简单,导航栏直接由角色表auth_role的角色描述vRoleDesc(父结点)和角色相关权限中的权限描述(标记为导航结点)vPermissionDesc(展开子项)组成。
JavaWeb项目:Shiro实现简单的权限控制(整合SSM)

Shiro相关配置

登录相关

首先,扩展AuthorizingRealm:用于从数据库抓取用户密码凭证交给Shiro进行验证,验证通过后获取用户拥有的角色信息和权限信息

public class UserRealm extends AuthorizingRealm{
@Autowired
private UserService userService;

//获取用户角色信息和权限信息,以代后续进行访问控制 
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
	String userName=(String)principals.getPrimaryPrincipal();
	
	SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
	authorizationInfo.setRoles(userService.findPermissionsByUserName(userName));//获取角色信息
	authorizationInfo.setStringPermissions(userService.findPermissionsByUserName(userName));//获取权限信息
	
	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();
	}
	//把密码查询出来,封装成SimpleAuthenticationInfo返回给Shiro进行验证
	SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(
			user.getUserName(),
			user.getPassword(),
			ByteSource.Util.bytes(user.getUserName()+user.getPasswordSalt()),
			getName());
	return authenticationInfo;
}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

扩展FormAuthenticationFilter:这里主要用于登录成功后获取用户的权限信息(导航栏),并将其存入session范围

public class WithNavibarFormAuthenticationFilter extends FormAuthenticationFilter {
@Autowired
private UserService userService;

@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
		ServletResponse response) throws Exception {
	HttpServletRequest httpReq=(HttpServletRequest)request;
	
	String userName=(String)SecurityUtils.getSubject().getPrincipal();
	List navigationBar=userService.getNavigationBar(userName);
	httpReq.getSession().setAttribute("navibar", navigationBar);
	return super.onLoginSuccess(token, subject, request, response);
}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

登录Controller实现,用户密码不匹配情况下执行,跳转回登录页面

@Controller
@RequestMapping("/user")
public class UserController {
	@Autowired
	private UserService userService;
	//...
@RequestMapping("/login")
public ModelAndView login(HttpServletRequest req){
	String error=null;
	String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
    if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
        error = "用户名/密码错误";
    } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
        error = "用户名/密码错误";
    } else if(exceptionClassName != null) {
        error = "其他错误:" + exceptionClassName;
    }
    ModelAndView mav=new ModelAndView("login");
    mav.addObject("error", error);
	return mav;
}
//...

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

登录表单代码:注意提交action="",以及输入控件name值须与form表单过滤器中的设置对应

<form class="form-signin" method="post" action="">
        <h3 class="form-signin-heading">请登录</h3>
        <label for="inputEmail" class="sr-only">用户名</label>
        <input type="text" id="inputEmail" class="form-control" placeholder="用户名" name="username" required autofocus>
        <label for="inputPassword" class="sr-only">密码</label>
        <input type="password" id="inputPassword" class="form-control" placeholder="密码" name="password" required>
        <div class="checkbox">
          <label>
            <input type="checkbox" name="rememberMe"> 记住我
          </label>
        </div>
        <p class="bg-warning">${error}</p>
        <button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
      </form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

重写配置密码匹配器,将密码输入次数缓存在ehcache,以进行重试次数的限制

//密码重试5次次数限制 
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher{
	private Cache<String,AtomicInteger> passwordRetryCache;
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager){
	passwordRetryCache=cacheManager.getCache("passwordRetryCache");
}

@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
	String username=(String)token.getPrincipal();
	AtomicInteger retryCount=passwordRetryCache.get(username);
	if(retryCount==null){
		retryCount=new AtomicInteger(0);
		passwordRetryCache.put(username, retryCount);
	}
	if(retryCount.incrementAndGet()&gt;5){
		throw new ExcessiveAttemptsException();
	}
	boolean matches= super.doCredentialsMatch(token, info);//匹配密码
	if(matches){
		passwordRetryCache.remove(username);
	}
	return matches;
}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

XML相关配置:

<!-- 凭证(密码)匹配器 -->
    <bean id="credentialsMatcher" class="com.test.shiro.credentials.RetryLimitHashedCredentialsMatcher">
        <constructor-arg ref="cacheManager"/>
        <property name="hashAlgorithmName" value="md5"/><!--md5加密-->
        <property name="hashIterations" value="2"/><!--加密迭代两次-->
        <property name="storedCredentialsHexEncoded" value="true"/><!--为true时使用Hex编码(默认);false时使用Base64编码-->
    </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用缓存实现为ehcache,相关配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shiroCache" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
   xsi:noNamespaceSchemaLocation="ehcache.xsd">
    <diskStore path="java.io.tmpdir"/> 
    <cache name="passwordRetryCache"
        maxEntriesLocalHeap="2000"
        eternal="false"<!--非永久有效-->
        timeToIdleSeconds="3600"<!-- 对象空闲时间,即60min后失效-->
        timeToLiveSeconds="0"
        overflowToDisk="false"
        statistics="true">
    </cache>
    <!--...-->
</ehcache>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

最后可以通过注解的方式,控制访问

@RequiresPermissions("user:list")
	@RequestMapping("/list")
	public ModelAndView showUserList(){
		//
	}
  • 1
  • 2
  • 3
  • 4
  • 5

完整shiro配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       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
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <!-- 缓存管理器 使用Ehcache实现 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:config/ehcache.xml"/>
    </bean>
    <!-- 凭证(密码)匹配器 -->
    <bean id="credentialsMatcher" class="com.test.shiro.credentials.RetryLimitHashedCredentialsMatcher">
        <constructor-arg ref="cacheManager"/>
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="2"/>
        <property name="storedCredentialsHexEncoded" value="true"/>
    </bean>  
    <!-- Realm实现 -->
    <bean id="userRealm" class="com.test.shiro.realm.UserRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
        <property name="cachingEnabled" value="true"/>
        <property name="authenticationCachingEnabled" value="true"/>
        <property name="authenticationCacheName" value="authenticationCache"/>
        <property name="authorizationCachingEnabled" value="true"/>
        <property name="authorizationCacheName" value="authorizationCache"/>
    </bean>
    <!-- 会话ID生成器 -->
    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
&lt;!-- 会话Cookie模板 --&gt;
&lt;bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"&gt;
    &lt;constructor-arg value="sid"/&gt;
    &lt;property name="httpOnly" value="true"/&gt;
    &lt;property name="maxAge" value="180000"/&gt;
&lt;/bean&gt;

&lt;!-- 会话DAO --&gt;
&lt;bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"&gt;
    &lt;property name="activeSessionsCacheName" value="shiro-activeSessionCache"/&gt;
    &lt;property name="sessionIdGenerator" ref="sessionIdGenerator"/&gt;
&lt;/bean&gt;

&lt;!-- 会话验证调度器 --&gt;
&lt;bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"&gt;
    &lt;property name="sessionValidationInterval" value="1800000"/&gt;
    &lt;property name="sessionManager" ref="sessionManager"/&gt;
&lt;/bean&gt;

&lt;!-- 会话管理器 --&gt;
&lt;bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"&gt;
    &lt;property name="globalSessionTimeout" value="1800000"/&gt;
    &lt;property name="deleteInvalidSessions" value="true"/&gt;
    &lt;property name="sessionValidationSchedulerEnabled" value="true"/&gt;
    &lt;property name="sessionValidationScheduler" ref="sessionValidationScheduler"/&gt;
    &lt;property name="sessionDAO" ref="sessionDAO"/&gt;
    &lt;property name="sessionIdCookieEnabled" value="true"/&gt;
    &lt;property name="sessionIdCookie" ref="sessionIdCookie"/&gt;
&lt;/bean&gt;

&lt;!-- 安全管理器 --&gt;
&lt;bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"&gt;
    &lt;property name="realm" ref="userRealm"/&gt;
    &lt;property name="sessionManager" ref="sessionManager"/&gt;
    &lt;property name="cacheManager" ref="cacheManager"/&gt;
&lt;/bean&gt;

&lt;!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) --&gt;
&lt;bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"&gt;
    &lt;property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/&gt;
    &lt;property name="arguments" ref="securityManager"/&gt;
&lt;/bean&gt;
&lt;!-- 基于Form表单的身份验证过滤器 --&gt;
&lt;bean id="formAuthenticationFilter" class="com.test.shiro.filter.WithNavibarFormAuthenticationFilter"&gt;
    &lt;property name="usernameParam" value="username"/&gt;
    &lt;property name="passwordParam" value="password"/&gt;
    &lt;property name="rememberMeParam" value="rememberMe"/&gt;
    &lt;property name="loginUrl" value="/user/login"/&gt;&lt;!--注意value值为登录url--&gt;
&lt;/bean&gt;
&lt;!-- Shiro的Web过滤器 --&gt;
&lt;bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"&gt;
    &lt;property name="securityManager" ref="securityManager"/&gt;
    &lt;property name="loginUrl" value="/user/login"/&gt;
    &lt;property name="filters"&gt;
        &lt;util:map&gt;
            &lt;entry key="authc" value-ref="formAuthenticationFilter"/&gt;
        &lt;/util:map&gt;
    &lt;/property&gt;
    &lt;property name="filterChainDefinitions"&gt;
        &lt;value&gt;
            /=authc
            /index=authc
            /user/login=authc&lt;!-- 使用表单filter authc --&gt;
            /logout=logout
            /static/**=anon&lt;!-- 不拦截静态资源文件 --&gt;
            /**=user
        &lt;/value&gt;
    &lt;/property&gt;
&lt;/bean&gt;

&lt;!-- Shiro生命周期处理器--&gt;
&lt;bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/&gt;

</beans>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

#其他如dao、service、controller组件实现省略
完整代码见:https://github.com/crazylai1996/auth-control.git

        </div>
					<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-7b4cdcb592.css" rel="stylesheet">
            </div>
</article>

该demo整合Shiro的相关配置参考**开涛的博客**

#数据库表格相关设计
JavaWeb项目:Shiro实现简单的权限控制(整合SSM)

表格设计得比较简单,导航栏直接由角色表auth_role的角色描述vRoleDesc(父结点)和角色相关权限中的权限描述(标记为导航结点)vPermissionDesc(展开子项)组成。
JavaWeb项目:Shiro实现简单的权限控制(整合SSM)

Shiro相关配置

登录相关

首先,扩展AuthorizingRealm:用于从数据库抓取用户密码凭证交给Shiro进行验证,验证通过后获取用户拥有的角色信息和权限信息

public class UserRealm extends AuthorizingRealm{
@Autowired
private UserService userService;

//获取用户角色信息和权限信息,以代后续进行访问控制 
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
	String userName=(String)principals.getPrimaryPrincipal();
	
	SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
	authorizationInfo.setRoles(userService.findPermissionsByUserName(userName));//获取角色信息
	authorizationInfo.setStringPermissions(userService.findPermissionsByUserName(userName));//获取权限信息
	
	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();
	}
	//把密码查询出来,封装成SimpleAuthenticationInfo返回给Shiro进行验证
	SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(
			user.getUserName(),
			user.getPassword(),
			ByteSource.Util.bytes(user.getUserName()+user.getPasswordSalt()),
			getName());
	return authenticationInfo;
}

相关文章:

  • 2022-12-23
  • 2021-10-03
  • 2022-12-23
  • 2022-12-23
  • 2022-01-10
  • 2021-09-21
  • 2021-09-17
猜你喜欢
  • 2021-08-23
  • 2022-12-23
  • 2021-11-14
  • 2021-06-01
  • 2021-12-27
相关资源
相似解决方案