一.本文主要是本人对使用shiro方面的总结。重点在介绍shiro使用。
二.1.导入所需要的架包
maven导入
org.apache.shiro shiro-web 1.3.2 org.apache.shiro shiro-aspectj 1.3.2 org.apache.shiro shiro-spring 1.3.2 org.apache.shiro shiro-ehcache 1.3.2
2.shiro的xml配置
spring-context-shiro.xml
xml version="1.0" encoding="UTF-8"?>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"> id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"> id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> name="realm" ref="jdbcRealm"> name="cacheManager" ref="cacheManager"/> id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> name="securityManager" ref="securityManager"/> id="credentialsMatcher" class="shiro.RetryLimitHashedCredentialsMatcher"> ref="cacheManager" /> name="hashAlgorithmName" value="md5" /> name="hashIterations" value="3" /> name="storedCredentialsHexEncoded" value="true" /> id="jdbcRealm" class="shiro.JdbcRealmCustom"> name="credentialsMatcher"> class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> name="hashAlgorithmName" value="MD5"> name="hashIterations" value="1024"> name="authenticationCachingEnabled" value="true" /> name="authenticationCacheName" value="authenticationCache" /> name="authorizationCachingEnabled" value="true" /> name="authorizationCacheName" value="authorizationCache" /> id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter"> name="redirectUrl" value="/login" /> id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> name="securityManager" ref="securityManager"> name="loginUrl" value="/login"/> name="successUrl" value="/index"/> name="unauthorizedUrl" value="/unauth"/> name="filterChainDefinitions"> /resources/** = anon /resource/** = anon /login* = anon /register* = anon /logout = logout /admin*=authc , roles[admin] /** = authc
3.web的xml中过滤器配置,注意过滤器名称需要和配置文件中Bean中过滤器名称相同
shiroFilter org.springframework.web.filter.DelegatingFilterProxy targetFilterLifecycle true shiroFilter /*
shiro的配置基本完成。
三、使用讲解。
使用shiro一般需要自己实现 shiro中的 AuthorizingRealm 方法。也可以直接使用shiro自己实现的方法,
例如:JdbcRealm 本人实现的方法名称是:JdbcRealmCustom(有点类似了!),本人都做了尝试,感觉还是shiro自己实现的方法JdbcRealmCustom,对新手容易上手点。但是自己实现的时候在配置缓存时候方便。
3.1 使用shiro自己实现的方法JdbcRealm,这个方法思路是直接写sql语句获得user信息,其他不用理会,shiro已经自己实现了
,直接看源码就明白使用方式了:
//TODO - complete JavaDoc /*-------------------------------------------- | C O N S T A N T S | ============================================*/ /** * 定义了一些常量,直接看sql语句 通过用户名获得用户密码,我这需要覆盖,因为表名不一样,以下几个同理 */ protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?"; /** * 获得密码+盐 */ protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?"; /** * 要角色 */ protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?"; /** * The default query used to retrieve permissions that apply to a particular role. */ protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?"; private static final Logger log = LoggerFactory.getLogger(JdbcRealm.class); /** * Password hash salt configuration.* NO_SALT - password hashes are not salted. * CRYPT - password hashes are stored in unix crypt format. * COLUMN - salt is in a separate column in the database. * EXTERNAL - salt is not stored in the database. {@link #getSaltForUser(String)} will be called * to get the salt */ public enum SaltStyle {NO_SALT, CRYPT, COLUMN, EXTERNAL}; /*-------------------------------------------- | I N S T A N C E V A R I A B L E S | ============================================*/ protected DataSource dataSource; protected String authenticationQuery = DEFAULT_AUTHENTICATION_QUERY; protected String userRolesQuery = DEFAULT_USER_ROLES_QUERY; protected String permissionsQuery = DEFAULT_PERMISSIONS_QUERY; protected boolean permissionsLookupEnabled = false; protected SaltStyle saltStyle = SaltStyle.NO_SALT; /*-------------------------------------------- | C O N S T R U C T O R S | ============================================*/ /*-------------------------------------------- | A C C E S S O R S / M O D I F I E R S | ============================================*/ /** * Sets the datasource that should be used to retrieve connections used by this realm. *设置数据库,老一套了 * @param dataSource the SQL data source. */ public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } /** * Overrides the default query used to retrieve a user's password during authentication. When using the default * implementation, this query must take the user's username as a single parameter and return a single result * with the user's password as the first column. If you require a solution that does not match this query * structure, you can override {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)} or * just {@link #getPasswordForUser(java.sql.Connection,String)} * 对外set方法了可以覆盖, * @param authenticationQuery the query to use for authentication. * @see #DEFAULT_AUTHENTICATION_QUERY */ public void setAuthenticationQuery(String authenticationQuery) { this.authenticationQuery = authenticationQuery; } /** * Overrides the default query used to retrieve a user's roles during authorization. When using the default * implementation, this query must take the user's username as a single parameter and return a row * per role with a single column containing the role name. If you require a solution that does not match this query * structure, you can override {@link #doGetAuthorizationInfo(PrincipalCollection)} or just * {@link #getRoleNamesForUser(java.sql.Connection,String)} * * @param userRolesQuery the query to use for retrieving a user's roles. * @see #DEFAULT_USER_ROLES_QUERY */ public void setUserRolesQuery(String userRolesQuery) { this.userRolesQuery = userRolesQuery; } /** * Overrides the default query used to retrieve a user's permissions during authorization. When using the default * implementation, this query must take a role name as the single parameter and return a row * per permission with three columns containing the fully qualified name of the permission class, the permission * name, and the permission actions (in that order). If you require a solution that does not match this query * structure, you can override {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} or just * {@link #getPermissions(java.sql.Connection,String,java.util.Collection)} * * Permissions are only retrieved if you set {@link #permissionsLookupEnabled} to true. Otherwise, * this query is ignored. * * @param permissionsQuery the query to use for retrieving permissions for a role. * @see #DEFAULT_PERMISSIONS_QUERY * @see #setPermissionsLookupEnabled(boolean) */ public void setPermissionsQuery(String permissionsQuery) { this.permissionsQuery = permissionsQuery; } /** * Enables lookup of permissions during authorization. The default is "false" - meaning that only roles * are associated with a user. Set this to true in order to lookup roles and permissions. * * @param permissionsLookupEnabled true if permissions should be looked up during authorization, or false if only * roles should be looked up. */ public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) { this.permissionsLookupEnabled = permissionsLookupEnabled; } /** * Sets the salt style. See {@link #saltStyle}. * * @param saltStyle new SaltStyle to set. */ public void setSaltStyle(SaltStyle saltStyle) { this.saltStyle = saltStyle; if (saltStyle == SaltStyle.COLUMN && authenticationQuery.equals(DEFAULT_AUTHENTICATION_QUERY)) { authenticationQuery = DEFAULT_SALTED_AUTHENTICATION_QUERY; } }
源码内容挺简单的,现在开始覆盖了。方式自然是xml加载bean了,IOC容器了,最简单,直接配置好spring启动时候就会自动 就加载完成。
配置过程上面spring-context-shiro.xml有,主要是这一块
name="dataSource" ref="dataSource"> name="authenticationQuery" value="SELECT password FROM T_USER WHERE user_name = ?"> name="userRolesQuery" value="select T_ROLE.ROLE_NAME from T_USER,T_USER_ROLE,T_ROLE where T_USER.USER_NAME= ? and T_USER.USER_ID=T_USER_ROLE.USER_ID and T_USER_ROLE.USER_ID=T_ROLE.ROLE_ID"> name="permissionsQuery" value="select T_RIGHT.RIGHT_NAME from T_ROLE,T_ROLE_RIGHT,T_RIGHT where T_ROLE.ROLE_NAME= ? and T_ROLE.ROLE_ID=T_ROLE_RIGHT.ROLE_ID and T_ROLE_RIGHT.RIGHT_ID=T_RIGHT.RIGHT_ID">
实际是sql语句书写,完成这一步时候,就要开始写登录部分了,shiro只是做权限、用户验证,主体逻辑还是要自己写,也很简单
@RequestMapping(value = "/login",method = { RequestMethod.GET, RequestMethod.POST }) public String login(User user,Model model) { if (isRelogin(user)){ return "index"; } //用户为空时,直接跳转登录页面:解决启动为空登录报错 if (StringUtil.isEmpty(user.getUserName())){ model.addAttribute("failMsg", "用户不能为空!"); return "login"; } // 组装token,包括用户名、密码 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUserName(),user.getPassword()); Subject subject = SecurityUtils.getSubject(); // shiro登陆验证 try { subject.login(usernamePasswordToken); org.apache.shiro.session.Session session = subject.getSession(); session.setAttribute("userName", user.getUserName()); } catch (Exception ex) { model.addAttribute("failMsg", "用户不存在或密码错误!"); return"login"; } return "index"; }
shiro将用户、密码组装成token,之后获得用户subject执行内部login方法,最终回去你xml配置的
name="realm" ref="jdbcRealm">
中进行登录的验证。同时shiro提供了各种抛出异常,可以使得异常提示更加准确。这里不详细叙述了。
至此第一方式完成。
3.2 使用自己实现的方法JdbcRealmCustom方法。
实际上和第一种非常类似,只要在配置文件中进行更改就,现在配置文件就是使用的第二种方式。
public class JdbcRealmCustom extends AuthorizingRealm { private Logger logger = LoggerFactory.getLogger("JdbcRealmCustom"); @Autowired private UserService userService; @Autowired private EhCacheManager ehCacheManager; private static String userName; private User user; /** * 用户授权认证 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 从数据库中获取当前登录用户的详细信息 if (user!=null){ // 根据用户名称获得用户角色 一个用户可以拥有多个角色 ListuserRoles=userService.getRoleByUserId(user.getUserId()); Set rightList= new HashSet ();; Set roleList = new HashSet ();; List rightNames ; for (KeyValudBean userRole:userRoles) { roleList.add(userRole.getValue()); //查询对应角色的对应权限集合 rightNames=userService.getRightByRoleId(userRole.getKey()); for (KeyValudBean rightName : rightNames) { if(StringUtils.isNotBlank(rightName.getValue())){ rightList.add(rightName.getValue()); } } } //赋角色 info.addRoles(roleList); //赋权限 info.addStringPermissions(rightList); return info; } return null; } /** * 用户登陆认证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 如果已经登陆,无需重新登录 //首先从缓存中获取记录 EhCache cache = (EhCache) ehCacheManager.getCache("userNameAndPass"); logger.info("======用户登陆认证======"); //UsernamePasswordToken对象用来存放提交的登录信息 userName = authenticationToken.getPrincipal().toString(); String password; if (StringUtil.isEmpty((String)cache.get(userName))) { user=userService.queryUserByName(userName); password=user.getPassword(); }else{ password=(String)cache.get(userName); System.err.printf("**********************去缓存获得密码******************************"); } //查出是否有此用户 // 缓存用户名和密码 cache.put(userName,password); if(user!=null || StringUtil.isNotEmpty(password)){ //若存在,将此用户存放到登录认证info中 return new SimpleAuthenticationInfo(userName, password, getName()); } return null; } /** * @Author: 郭航飞 * @Description::已经登录处理 * @CreateDate: 2018/4/25 17:55 **/ private boolean isRelogin(User user) { Subject us = SecurityUtils.getSubject(); if (us.isAuthenticated()) { // 参数未改变,无需重新登录,默认为已经登录成功 return true; } // 需要重新登陆 return false; } }
对比一下非常类似,只是更改成用java代码去层层调用获得数据。
四.对于缓存,暂时没有深入研究。就是简单的实现了一个样例。
所用源码在 https://github.com/guohangfei/my_project
五、总结:
两种方式都可以,新手建议先使用第一种进行使用。实际业务量不多情况第一种其实简单实用。