1.首先创建自己的MyShiroRealm 继承自JdbcRealm 来其目的是可在必要情况下实现自定义的认证授权策略。
public
class MyShiroRealm
extends JdbcRealm {
@Resource
private XAccountService accountService;
/**
* 认证回调函数
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken){
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
Account account = accountService.selectByName(username);
if (account == null) {
throw new AccountNoneException();
}
@Resource
private XAccountService accountService;
/**
* 认证回调函数
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken){
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
Account account = accountService.selectByName(username);
if (account == null) {
throw new AccountNoneException();
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(account.getUsername(),
account.getPassword().toCharArray(), getName());
if (StringUtils.isNotBlank(account.getSalt()))
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(account.getSalt()));
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(account.getSalt()));
return authenticationInfo;
}
}
2.关于认证
在
MyShiroRealm 的
doGetAuthenticationInfo方法中 token储存着当前登录的账号和加密前的明文密码,经过上述处理会与数据库密码进行匹配。
那么问题来了,shiro是如何对用户传进来的密码进行匹配验证的呢?请接着往下看:
在shiro的配置文件中有一个凭证匹配器
<!-- 凭证匹配器 暂时去掉缓存 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- <constructor-arg ref="ehCacheShiro"/> -->
<property name="hashAlgorithmName" value="SHA-512"/> 指定hash算法为MD5;
<property name="hashIterations" value="1024"/> 指定散列次数为2次;
<property name="storedCredentialsHexEncoded" value="true"/>指定Hash散列值使用Hex加密存. value="false"表明hash散列值用用Base64-encoded存储。
</bean>
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- <constructor-arg ref="ehCacheShiro"/> -->
<property name="hashAlgorithmName" value="SHA-512"/> 指定hash算法为MD5;
<property name="hashIterations" value="1024"/> 指定散列次数为2次;
<property name="storedCredentialsHexEncoded" value="true"/>指定Hash散列值使用Hex加密存. value="false"表明hash散列值用用Base64-encoded存储。
</bean>
以上配置告诉shiro将如何对用户传来的明文密码进行加密
源码:
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
Object tokenHashedCredentials = hashProvidedCredentials(token, info);
Object accountCredentials = getCredentials(info);
return equals(tokenHashedCredentials, accountCredentials);
}
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
Object tokenHashedCredentials = hashProvidedCredentials(token, info);
Object accountCredentials = getCredentials(info);
return equals(tokenHashedCredentials, accountCredentials);
}
protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
Object salt = null;
if (info instanceof SaltedAuthenticationInfo) {
salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
} else {
//retain 1.0 backwards compatibility:
if (isHashSalted()) {
salt = getSalt(token);
}
}
return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
}
Object salt = null;
if (info instanceof SaltedAuthenticationInfo) {
salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
} else {
//retain 1.0 backwards compatibility:
if (isHashSalted()) {
salt = getSalt(token);
}
}
return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
}
当然注册的时候也要使用相同的加密规则,否则shiro认证不会通过。
你也可以自定义自己的凭证匹配器实现
doCredentialsMatch方法,进行密码匹配
<!-- 凭证匹配器 暂时去掉缓存 -->
<bean id="credentialsMatcher" class="com.luming.shiro.credentials.RetryLimitHashedCredentialsMatcher"></bean>
<bean id="credentialsMatcher" class="com.luming.shiro.credentials.RetryLimitHashedCredentialsMatcher"></bean>
自定义的RetryLimitHashedCredentialsMatcher类
public
class RetryLimitHashedCredentialsMatcher
extends HashedCredentialsMatcher {
// private Cache<String, AtomicInteger> passwordRetryCache;
public RetryLimitHashedCredentialsMatcher() {
super();
}
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
//passwordRetryCache = cacheManager.getCache("passwordRetryCache");
super();
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken usernamePasswordToken = null;
try {
if (token instanceof UsernamePasswordToken) {
usernamePasswordToken = (UsernamePasswordToken) token;
if (info instanceof SaltedAuthenticationInfo) {
//DB登录密码
String passwordCredentials = new String(( char [] )info.getCredentials()) ;
//DB salt
String salt = new String(((SimpleByteSource)(((SaltedAuthenticationInfo)info).getCredentialsSalt())).getBytes());
//DB mobileNumber
// String mobileNumber = ((SimplePrincipalCollection) info.getPrincipals()).toString();
//用户User
String encodePassword = encodePassword( new String(usernamePasswordToken.getPassword()),salt);
return StringUtils.equals(passwordCredentials, encodePassword) ;
}
}
} catch (Exception e) {
System.out.println(e.toString());
}
return false;
}
// private Cache<String, AtomicInteger> passwordRetryCache;
public RetryLimitHashedCredentialsMatcher() {
super();
}
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
//passwordRetryCache = cacheManager.getCache("passwordRetryCache");
super();
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken usernamePasswordToken = null;
try {
if (token instanceof UsernamePasswordToken) {
usernamePasswordToken = (UsernamePasswordToken) token;
if (info instanceof SaltedAuthenticationInfo) {
//DB登录密码
String passwordCredentials = new String(( char [] )info.getCredentials()) ;
//DB salt
String salt = new String(((SimpleByteSource)(((SaltedAuthenticationInfo)info).getCredentialsSalt())).getBytes());
//DB mobileNumber
// String mobileNumber = ((SimplePrincipalCollection) info.getPrincipals()).toString();
//用户User
String encodePassword = encodePassword( new String(usernamePasswordToken.getPassword()),salt);
return StringUtils.equals(passwordCredentials, encodePassword) ;
}
}
} catch (Exception e) {
System.out.println(e.toString());
}
return false;
}
/**
* 哈希加密并Base64处理密码
* @param rawPassword
* @param salt
* @return
*/
public static String encodePassword(String rawPassword, String salt) {
return new Sha512Hash(rawPassword, salt,512).toBase64();
}
}
* 哈希加密并Base64处理密码
* @param rawPassword
* @param salt
* @return
*/
public static String encodePassword(String rawPassword, String salt) {
return new Sha512Hash(rawPassword, salt,512).toBase64();
}
}
3.关于授权
在shiro配置文件中有如下配置
<!-- security data source: -->
<bean id="xkeshiMysqlRealm" class="com.xkeshi.shiro.MyShiroRealm" >
<property name="dataSource" ref="dataSource"/>
<property name="authenticationCachingEnabled" value="false"/>
<property name="authorizationCacheName" value="authorizationCacheName"/>
<property name="userRolesQuery" value="SELECT r.value FROM role r LEFT JOIN account_role ar ON r.id = ar.role_id LEFT JOIN account a ON a.id = ar.account_id WHERE a.username = ? ORDER BY a.createDate DESC LIMIT 0,1 "/>
<property name="authenticationQuery" value="select password from Account where username = ? and deleted=false and enable = true "/>
<property name="userRolesQuery" value="SELECT r.value FROM role r LEFT JOIN account_role ar ON r.id = ar.role_id LEFT JOIN account a ON a.id = ar.account_id WHERE a.username = ? "/>
<property name="permissionsQuery" value="SELECT permission_code FROM role_permission LEFT JOIN role ON role_permission.role_id = role.id WHERE role.name = ?"/>
<!-- <property name="permissionsLookupEnabled" value="true"/> -->
</bean>
这些配置主要用于当使用shiro原生的认证授权规则时告诉shiro该如何根据用户账号来查询一些信息(如用户密文密码,角色信息,权限信息)以便于认证及授权(账号已通过
UsernamePasswordToken传入)
<bean id="xkeshiMysqlRealm" class="com.xkeshi.shiro.MyShiroRealm" >
<property name="dataSource" ref="dataSource"/>
<property name="authenticationCachingEnabled" value="false"/>
<property name="authorizationCacheName" value="authorizationCacheName"/>
<property name="userRolesQuery" value="SELECT r.value FROM role r LEFT JOIN account_role ar ON r.id = ar.role_id LEFT JOIN account a ON a.id = ar.account_id WHERE a.username = ? ORDER BY a.createDate DESC LIMIT 0,1 "/>
<property name="authenticationQuery" value="select password from Account where username = ? and deleted=false and enable = true "/>
<property name="userRolesQuery" value="SELECT r.value FROM role r LEFT JOIN account_role ar ON r.id = ar.role_id LEFT JOIN account a ON a.id = ar.account_id WHERE a.username = ? "/>
<property name="permissionsQuery" value="SELECT permission_code FROM role_permission LEFT JOIN role ON role_permission.role_id = role.id WHERE role.name = ?"/>
<!-- <property name="permissionsLookupEnabled" value="true"/> -->
</bean>
有几个比较重要的列举下:
(1)
authenticationQuery 从数据库中查询出密文密码用于认证(自定义认证效果下可如上文中那样从UsernamePasswordToken中获得账号再通过账号查询出密文密码)
源代码如下:
protected
AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws
AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
// Null username is invalid
if (username == null ) {
throw new AccountException("Null usernames are not allowed by this realm.");
}
Connection conn = null ;
SimpleAuthenticationInfo info = null ;
try {
conn = dataSource.getConnection();
String password = null ;
String salt = null ;
switch (saltStyle) {
case NO_SALT:
password = getPasswordForUser(conn, username)[0];
break ;
case CRYPT:
// TODO: separate password and hash from getPasswordForUser[0]
throw new ConfigurationException("Not implemented yet");
//break;
case COLUMN:
String[] queryResults = getPasswordForUser(conn, username);
password = queryResults[0];
salt = queryResults[1];
break ;
case EXTERNAL:
password = getPasswordForUser(conn, username)[0];
salt = getSaltForUser(username);
}
if (password == null ) {
throw new UnknownAccountException("No account found for user [" + username + "]");
}
info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());
if (salt != null ) {
info.setCredentialsSalt(ByteSource.Util.bytes(salt));
}
} catch (SQLException e) {
final String message = "There was a SQL error while authenticating user [" + username + "]";
if (log.isErrorEnabled()) {
log.error(message, e);
}
// Rethrow any SQL errors as an authentication exception
throw new AuthenticationException(message, e);
} finally {
JdbcUtils.closeConnection(conn);
}
return info;
}
private String[] getPasswordForUser(Connection conn, String username) throws SQLException {
String[] result;
boolean returningSeparatedSalt = false ;
switch (saltStyle) {
case NO_SALT:
case CRYPT:
case EXTERNAL:
result = new String[1];
break ;
default :
result = new String[2];
returningSeparatedSalt = true ;
}
PreparedStatement ps = null ;
ResultSet rs = null ;
try {
ps = conn.prepareStatement( authenticationQuery );
ps.setString(1, username);
// Execute query
rs = ps.executeQuery();
// Loop over results - although we are only expecting one result, since usernames should be unique
boolean foundResult = false ;
while (rs.next()) {
// Check to ensure only one row is processed
if (foundResult) {
throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique.");
}
result[0] = rs.getString(1);
if (returningSeparatedSalt) {
result[1] = rs.getString(2);
}
foundResult = true ;
}
} finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(ps);
}
return result;
}
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
// Null username is invalid
if (username == null ) {
throw new AccountException("Null usernames are not allowed by this realm.");
}
Connection conn = null ;
SimpleAuthenticationInfo info = null ;
try {
conn = dataSource.getConnection();
String password = null ;
String salt = null ;
switch (saltStyle) {
case NO_SALT:
password = getPasswordForUser(conn, username)[0];
break ;
case CRYPT:
// TODO: separate password and hash from getPasswordForUser[0]
throw new ConfigurationException("Not implemented yet");
//break;
case COLUMN:
String[] queryResults = getPasswordForUser(conn, username);
password = queryResults[0];
salt = queryResults[1];
break ;
case EXTERNAL:
password = getPasswordForUser(conn, username)[0];
salt = getSaltForUser(username);
}
if (password == null ) {
throw new UnknownAccountException("No account found for user [" + username + "]");
}
info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());
if (salt != null ) {
info.setCredentialsSalt(ByteSource.Util.bytes(salt));
}
} catch (SQLException e) {
final String message = "There was a SQL error while authenticating user [" + username + "]";
if (log.isErrorEnabled()) {
log.error(message, e);
}
// Rethrow any SQL errors as an authentication exception
throw new AuthenticationException(message, e);
} finally {
JdbcUtils.closeConnection(conn);
}
return info;
}
private String[] getPasswordForUser(Connection conn, String username) throws SQLException {
String[] result;
boolean returningSeparatedSalt = false ;
switch (saltStyle) {
case NO_SALT:
case CRYPT:
case EXTERNAL:
result = new String[1];
break ;
default :
result = new String[2];
returningSeparatedSalt = true ;
}
PreparedStatement ps = null ;
ResultSet rs = null ;
try {
ps = conn.prepareStatement( authenticationQuery );
ps.setString(1, username);
// Execute query
rs = ps.executeQuery();
// Loop over results - although we are only expecting one result, since usernames should be unique
boolean foundResult = false ;
while (rs.next()) {
// Check to ensure only one row is processed
if (foundResult) {
throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique.");
}
result[0] = rs.getString(1);
if (returningSeparatedSalt) {
result[1] = rs.getString(2);
}
foundResult = true ;
}
} finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(ps);
}
return result;
}
(2)userRolesQuery 从数据库中查询出账号的角色信息
(3)permissionsQuery 从数据库中查询出账号的权限信息
源码如下:
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//null usernames are invalid
if (principals == null) {
throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
}
String username = (String) getAvailablePrincipal(principals);
Connection conn = null;
Set<String> roleNames = null;
Set<String> permissions = null;
try {
conn = dataSource.getConnection();
// Retrieve roles and permissions from database
roleNames = getRoleNamesForUser(conn, username);
if (permissionsLookupEnabled) {
permissions = getPermissions(conn, username, roleNames);
}
} catch (SQLException e) {
final String message = "There was a SQL error while authorizing user [" + username + "]";
if (log.isErrorEnabled()) {
log.error(message, e);
}
// Rethrow any SQL errors as an authorization exception
throw new AuthorizationException(message, e);
} finally {
JdbcUtils.closeConnection(conn);
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
info.setStringPermissions(permissions);
return info;
}
protected Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException {
PreparedStatement ps = null;
ResultSet rs = null;
Set<String> roleNames = new LinkedHashSet<String>();
try {
ps = conn.prepareStatement( userRolesQuery );
ps.setString(1, username);
// Execute query
rs = ps.executeQuery();
// Loop over results and add each returned role to a set
while (rs.next()) {
String roleName = rs.getString(1);
// Add the role to the list of names if it isn't null
if (roleName != null ) {
roleNames.add(roleName);
} else {
if (log.isWarnEnabled()) {
log.warn("Null role name found while retrieving role names for user [" + username + "]");
}
}
}
} finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(ps);
}
return roleNames;
}
protected Set<String> getPermissions(Connection conn, String username, Collection<String> roleNames) throws SQLException{
PreparedStatement ps = null ;
Set<String> permissions = new LinkedHashSet<String>();
try {
ps = conn.prepareStatement( permissionsQuery );
for (String roleName : roleNames) {
ps.setString(1, roleName);
ResultSet rs = null ;
try {
// Execute query
rs = ps.executeQuery();
// Loop over results and add each returned role to a set
while (rs.next()) {
String permissionString = rs.getString(1);
// Add the permission to the set of permissions
permissions.add(permissionString);
}
} finally {
JdbcUtils.closeResultSet(rs);
}
}
} finally {
JdbcUtils.closeStatement(ps);
}
return permissions;
}
以上两个配置即shiro原生授权策略
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//null usernames are invalid
if (principals == null) {
throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
}
String username = (String) getAvailablePrincipal(principals);
Connection conn = null;
Set<String> roleNames = null;
Set<String> permissions = null;
try {
conn = dataSource.getConnection();
// Retrieve roles and permissions from database
roleNames = getRoleNamesForUser(conn, username);
if (permissionsLookupEnabled) {
permissions = getPermissions(conn, username, roleNames);
}
} catch (SQLException e) {
final String message = "There was a SQL error while authorizing user [" + username + "]";
if (log.isErrorEnabled()) {
log.error(message, e);
}
// Rethrow any SQL errors as an authorization exception
throw new AuthorizationException(message, e);
} finally {
JdbcUtils.closeConnection(conn);
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
info.setStringPermissions(permissions);
return info;
}
protected Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException {
PreparedStatement ps = null;
ResultSet rs = null;
Set<String> roleNames = new LinkedHashSet<String>();
try {
ps = conn.prepareStatement( userRolesQuery );
ps.setString(1, username);
// Execute query
rs = ps.executeQuery();
// Loop over results and add each returned role to a set
while (rs.next()) {
String roleName = rs.getString(1);
// Add the role to the list of names if it isn't null
if (roleName != null ) {
roleNames.add(roleName);
} else {
if (log.isWarnEnabled()) {
log.warn("Null role name found while retrieving role names for user [" + username + "]");
}
}
}
} finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(ps);
}
return roleNames;
}
protected Set<String> getPermissions(Connection conn, String username, Collection<String> roleNames) throws SQLException{
PreparedStatement ps = null ;
Set<String> permissions = new LinkedHashSet<String>();
try {
ps = conn.prepareStatement( permissionsQuery );
for (String roleName : roleNames) {
ps.setString(1, roleName);
ResultSet rs = null ;
try {
// Execute query
rs = ps.executeQuery();
// Loop over results and add each returned role to a set
while (rs.next()) {
String permissionString = rs.getString(1);
// Add the permission to the set of permissions
permissions.add(permissionString);
}
} finally {
JdbcUtils.closeResultSet(rs);
}
}
} finally {
JdbcUtils.closeStatement(ps);
}
return permissions;
}
自定义授权(只是简单的写了下,实际上需要通过UsernamePasswordToken中的账号信息查询出角色,及权限)
/**
* 授权实现
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principals) {
System.out.println("进入授权管理");
String userName = (String) super.getAvailablePrincipal(principals);
if (userName != null){
System.out.println("当前登录账号为:"+userName);
List<String> roleList = new ArrayList<String>();
List<String> permissionList = new ArrayList<String>();
roleList.add("common");
roleList.add("spic");
permissionList.add("user:show");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRoles(roleList);
simpleAuthorizationInfo.addStringPermissions(permissionList);
return simpleAuthorizationInfo;
} else {
return null;
}
}
最后在shiro配置文件中配置如下即可启用授权管理:
* 授权实现
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principals) {
System.out.println("进入授权管理");
String userName = (String) super.getAvailablePrincipal(principals);
if (userName != null){
System.out.println("当前登录账号为:"+userName);
List<String> roleList = new ArrayList<String>();
List<String> permissionList = new ArrayList<String>();
roleList.add("common");
roleList.add("spic");
permissionList.add("user:show");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRoles(roleList);
simpleAuthorizationInfo.addStringPermissions(permissionList);
return simpleAuthorizationInfo;
} else {
return null;
}
}
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--未登录状态下访问authc则进入loginUrl配置的路径-->
<property name="loginUrl" value="/login"></property>
<!--不知道怎么用-->
<!--<property name="successUrl" value="success"></property>-->
<!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
<property name="unauthorizedUrl" value="400"></property>
<property name="filterChainDefinitions">
<!--
anon :不用登陆也能访问
authc:登录之后才能访问 未登录状态下自动跳到loginUrl配置的路径
logout :
authcBasic:httpBasic认证
ssl:安全的URL请求,协议为https
port[8081] :当请求的URL端口不是8081时,跳转到schemal: //serverName:8081?queryString
rest[user] :根据请求的方法,相当于/admins/user/**=perms[user:method],其中method为post,get,delete等
perms[role:view/edit] 判断角色或权限,不足则执行 unauthorizedUrl
roles[admin] : 当有多个参数时必须每个参数都通过才算通过,相当于hasAllRoles()方法
-->
<value>
/login/** = anon
/favicon.ico = anon
/spic/** =roles[spic]
/common/** =roles[common]
/user/** =perms[user:show]
/third/** =roles[third]
/api/** = anon
/upyun/notify = anon
/balance/api/** = anon
/umpay/api/** = anon
/image/code = anon
/ static/** = anon
/** = authc
</value>
</property>
</bean>
<property name="securityManager" ref="securityManager"/>
<!--未登录状态下访问authc则进入loginUrl配置的路径-->
<property name="loginUrl" value="/login"></property>
<!--不知道怎么用-->
<!--<property name="successUrl" value="success"></property>-->
<!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
<property name="unauthorizedUrl" value="400"></property>
<property name="filterChainDefinitions">
<!--
anon :不用登陆也能访问
authc:登录之后才能访问 未登录状态下自动跳到loginUrl配置的路径
logout :
authcBasic:httpBasic认证
ssl:安全的URL请求,协议为https
port[8081] :当请求的URL端口不是8081时,跳转到schemal: //serverName:8081?queryString
rest[user] :根据请求的方法,相当于/admins/user/**=perms[user:method],其中method为post,get,delete等
perms[role:view/edit] 判断角色或权限,不足则执行 unauthorizedUrl
roles[admin] : 当有多个参数时必须每个参数都通过才算通过,相当于hasAllRoles()方法
-->
<value>
/login/** = anon
/favicon.ico = anon
/spic/** =roles[spic]
/common/** =roles[common]
/user/** =perms[user:show]
/third/** =roles[third]
/api/** = anon
/upyun/notify = anon
/balance/api/** = anon
/umpay/api/** = anon
/image/code = anon
/ static/** = anon
/** = authc
</value>
</property>
</bean>
插播一条:不知是我各人理解错误还是其他,经测试,授权策略在用户访问受权限控制的请求时都会重新进行一次用户授权操作,也就是说用户每次访问受限请求都会去数据库查询,个人认为没有必要。
解决方案:事实上没有必要把所有的受限请求都配置到配置文件中,只需一个入口即可,由于shiro会将权限信息存在Subject中,对于其他受限请求可以自定义一个注解和拦截器,拦截那些受限制的请求,根据注解中要求的权限角色信息判断是否拦截该请求。
已权限为例代码如下:
/**
* 自定义的权限注解
* @author lm
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface CheckPermission {
// 可以传多个权限标示
String[] permission();
* 自定义的权限注解
* @author lm
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface CheckPermission {
// 可以传多个权限标示
String[] permission();
/**
* 权限拦截器
* @author lm
*
*
*/
public class PermissionInterceptorAdapter extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handler2 = (HandlerMethod) handler;
CheckPermission checkPermission = handler2 .getMethodAnnotation(CheckPermission.class);
boolean isPermissioin = false;
Subject currentUser = SecurityUtils.getSubject();
// 没有获得注解 及不需要权限-- 则直接运行
if ( null != checkPermission) {
String[] permission = checkPermission.permission();
for (String per : permission) {
// 当前登录人 具有权限
if (currentUser.isPermitted(per)) {
isPermissioin = true;
break;
}
}
} else {
isPermissioin = true;
}
//System.out.println("拦截到了mvc方法:" + handler2.getMethod());
if (isPermissioin) {
// 有执行方法或权限不拦截
return true;
} else {
// 无权限异常
response.sendRedirect("/error");
throw new AuthorizationException();
}
}
}
这样在controller层方法前加上类似@CheckPermission(permission ={"add","edit"})注解即可。
* 权限拦截器
* @author lm
*
*
*/
public class PermissionInterceptorAdapter extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handler2 = (HandlerMethod) handler;
CheckPermission checkPermission = handler2 .getMethodAnnotation(CheckPermission.class);
boolean isPermissioin = false;
Subject currentUser = SecurityUtils.getSubject();
// 没有获得注解 及不需要权限-- 则直接运行
if ( null != checkPermission) {
String[] permission = checkPermission.permission();
for (String per : permission) {
// 当前登录人 具有权限
if (currentUser.isPermitted(per)) {
isPermissioin = true;
break;
}
}
} else {
isPermissioin = true;
}
//System.out.println("拦截到了mvc方法:" + handler2.getMethod());
if (isPermissioin) {
// 有执行方法或权限不拦截
return true;
} else {
// 无权限异常
response.sendRedirect("/error");
throw new AuthorizationException();
}
}
}
第一次写博文,废话比较多,与君共勉!