转载自凌大大的博客,参考原博客原址:[关于用户登录和权限验证功能的实现步骤](http://blog.csdn.net/bolu1234/article/details/51867099)
接上一篇shiro框架—关于用户登录和权限验证功能的实现步骤(三)
关于shiro在springboot的配置,共有四个基本配置文件主要的文件有四个ShiroConfig
、RetryLimitHashedCredentialsMatcher
、UserRealm
、MShiroFilterFactoryBean
。
因为上一篇仅仅写完了shiroconfig
类的配置,虽然在上一篇最下边,我已经附上了所有文件的下载链接,但是我觉得还是要写一下剩下的三个配置文件,写一下我对shiro配置的理解。下边是剩余三个配置文件,即RetryLimitHashedCredentialsMatcher
、UserRealm
、MShiroFilterFactoryBean
的配置详情。
1.UserRealm
类的配置内容
具体代码如下:
package microservice.fpzj.shiro;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import microservice.fpzj.core.models.Permission;
import microservice.fpzj.core.models.Role;
import microservice.fpzj.core.models.User;
import microservice.fpzj.service.jwt.UserService;
/**
* 验证用户登录
*
* @author Administrator
*/
@Component("userRealm")
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
public UserRealm() {
setName("UserRealm");
// 采用MD5加密
setCredentialsMatcher(new HashedCredentialsMatcher("md5"));
}
/**
*权限资源角色配置,如果在jsp里加入了shiro的标签配置,将会在访问那个jsp页
* 面的时候触发这个方法,这里先不考虑这个方法的逻辑。
**/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
//登录验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upt = (UsernamePasswordToken) token;
char[] passwords = upt.getPassword();
String pwd = new String(passwords);
if(!StringUtils.isEmpty(pwd)){ //如果密码不是空,表示是本地登录
User user = this.userService.findUserByUserName(upt.getUsername());
if(user == null){
throw new AuthenticationException("no_user");
}
boolean res =true;
for(Role r:user.getRoles()){
if(r.getSiteid()==2){
res = false;
break;
}
}
if(res){
throw new AuthenticationException("no_permission");
}
if(user!=null && !user.isDeleted()){
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
user, //用户对象
user.getPasswd(), //密码
ByteSource.Util.bytes(user.getUserid()+user.getUsername()),//salt=username+salt
getName()); //realm name
return info;
}
return null;//getAuthenticationInfoMethod(upt.getUsername());
}else{
/**
*如果密码是空,表示是远程登录,从username位置获得token,
*暂时不考虑这种情况,这里只走上边if里边
**/
String username = userService.getUsernameFromToken(upt.getUsername());
return getAuthenticationInfoMethod(username);
}
}
public AuthenticationInfo getAuthenticationInfoMethod(String username){
User user = userService.findUserByUserName(username);
if(user == null){
throw new AuthenticationException("no_user");
}
if(user != null){
boolean res =true;
for(Role r:user.getRoles()){
if(r.getSiteid()==2){
res = false;
break;
}
}
if(res){
throw new AuthenticationException("no_permission");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
user, //用户对象
user.getPasswd(), //密码
ByteSource.Util.bytes(user.getUserid()+user.getUsername()),//salt=username+salt
getName()); //realm name
return info;
}
return null;
}
}
- 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
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
以上主要看登录验证方法doGetAuthenticationInfo
在该方法里,通过从自己写的userService
接口,先根据用户名查询出用户对象,即调用findUserByUserName
方法,获取用户对象下的角色roles
,通过判断用户下的角色集合中,是否含有siteId
为2的,如果含有siteId
为2的,则让通过登录,如果没有当前siteId
的角色,则登录失败,站点siteId
表示系统类别,2在我们多系统里为业务系统,还有门户系统、后台系统等。
siteId
我在第一篇里已经写了,shiro框架—关于用户登录和权限验证功能的实现步骤(一)
2.RetryLimitHashedCredentialsMatcher
类的配置内容
具体代码如下:
package microservice.fpzj.shiro;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import microservice.fpzj.core.models.User;
import microservice.fpzj.service.jwt.UserService;
/**
*
* @author ZML
*
*/
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
@Autowired
private UserService userService;
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
if(token instanceof UsernamePasswordToken){
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
User user = userService.findUserByUserName(userToken.getUsername());
char[] passwordchars =userToken.getPassword();
String password = new String(passwordchars);
if("".equals(password)){ //token字符串登录
boolean flg = userService.verifyToken(userToken.getUsername());
return flg;
}else{ //账号密码登录
boolean res = userService.passwordVerify(password,user.getPasswd(),user.getUserid()+user.getUsername());
return res;
}
}
return false;
}
}
- 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
上一篇已经说过,RetryLimitHashedCredentialsMatcher
类,继承于HashedCredentialsMatcher
类,对于HashedCredentialsMatcher
类,你如果往上找,就会发现,其实就是实现了CredentialsMatcher
接口,最后又在shiroConfig
配置文件中,将该类注入到我们的spring
容器中供UserRealm
调用,加入到shiro
框架里,这样就可以扩展我们自定义的这个RetryLimitHashedCredentialsMatcher
类,该类的功能主要是将用户输入的密码与查询到的密码进行比较,也就是密码比较器。
上边方法doCredentialsMatch
的逻辑里,有verifyToken
这样的方法,其实这个是针对于那种没有账号密码,只有token
字符串的密钥登录的方式。这里先不说这一种。
userService
里是我们自己提前写好的一系列针对于用户验证的接口,因为shiro
不会去维护用户、权限、角色这些东西,需要我们自己维护,关于建表的那些步骤,我在第一篇里已经写了,shiro框架—关于用户登录和权限验证功能的实现步骤(一),比如这里的passwordVerify
方法 ,通过名字可以理解,是验证密码的接口,这些会在下一篇写出来。
3.MShiroFilterFactoryBean
类的配置内容
还记得在上一篇文章shiro框架—关于用户登录和权限验证功能的实现步骤(三) 里,提到过 MShiroFilterFactoryBean
继承于 ShiroFilterFactoryBean
类,是我们自己自定义的shiroFilter
,该类在shiroConfig
里注入到spring
容器中,另外,因为shiro
的拦截是对所有请求进行拦截,关于.js
、.css
、.html
、.png
等静态资源的请求,应该是不要拦截的,所以我们需要在自定义shiroFilter
里对这些静态资源进行过滤,对我们项目来说,这就是这个MShiroFilterFactoryBean
的存在的意义。
具体配置如下:
package microservice.fpzj.shiro;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.BeanInitializationException;
/**
* 自定义shiro过滤规则
* @author Administrator
*
*/
public class MShiroFilterFactoryBean extends ShiroFilterFactoryBean{
// 对ShiroFilter来说,需要直接忽略的请求
private Set<String> ignoreExt;
public MShiroFilterFactoryBean() {
super();
ignoreExt = new HashSet<>();
ignoreExt.add(".jsp");
// ignoreExt.add(".png");
}
@Override
protected AbstractShiroFilter createInstance() throws Exception {
org.apache.shiro.mgt.SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
FilterChainManager manager = createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new MSpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
private final class MSpringShiroFilter extends AbstractShiroFilter {
protected MSpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
super();
if (webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
}
setSecurityManager(webSecurityManager);
if (resolver != null) {
setFilterChainResolver(resolver);
}
}
@Override
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
String str = request.getRequestURI().toLowerCase();
// 因为ShiroFilter 拦截所有请求(在上面我们配置了urlPattern 为 * ,当然你也可以在那里精确的添加要处理的路径,这样就不需要这个类了),而在每次请求里面都做了session的读取和更新访问时间等操作,这样在集群部署session共享的情况下,数量级的加大了处理量负载。
// 所以我们这里将一些能忽略的请求忽略掉。
// 当然如果你的集群系统使用了动静分离处理,静态资料的请求不会到Filter这个层面,便可以忽略。
boolean flag = true;
int idx = 0;
if(( idx = str.indexOf(".")) > 0 ){
str = str.substring(idx);
/**
*如果不是jsp则为false,即除了.jsp,其他的都不需要拦截
**/
if(!ignoreExt.contains(str.toLowerCase())){
flag = false;
}
}
if(flag){
super.doFilterInternal(servletRequest, servletResponse, chain);
}else{
chain.doFilter(servletRequest, servletResponse);
}
}
}
}
- 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
通过以上的配置,就会将所有不是.jsp
的又带有.
资源全部放开,即,没有登录的情况下,可以访问到,其他带有.
的资源则全部被shiro拦截。
4.注意点
以上的配置文件中,你会看到有很多的userService
中的方法,我没有贴出来,后边我也都会贴出来,但是,其实这样的逻辑基本都是数据库查询的逻辑。
比如:
下面的这个是根据用户名获取用户对象的方法,因为用户名是唯一的,这个应该很容易写出来,就是根据用户名字段去匹配用户表的一条对象记录。
User user = userService.findUserByUserName(upt.getUsername())
- 1
下面的这个是验证账号密码的方法。
boolean flag = userService.passwordVerify(password,user.getPasswd(),user.getUserid()+user.getUsername())
- 1
下面的这个是根据token字符串获取用户名,token是一种jwt字符串,是一种很长的字符串,这里主要用于那种没有用户名,只有一个令牌token字符串,通过这个token字符串,来到后台验证的时候,需要通过这个字符串转换成用户名,然后再根据用户名,调用上边的方法获取用户对象。这里先不考虑这种的登录,只考虑正常的那种账号密码的登录方式。
String username = userService.getUsernameFromToken(upt.getUsername())
- 1
下面的这个是验证客户端传入的token字符串是否有效的接口。同样这里不考虑这种的登录方式。
boolean flag = userService.verifyToken(userToken.getUsername())
- 1
转载自凌大大的博客,参考原博客原址:[关于用户登录和权限验证功能的实现步骤](http://blog.csdn.net/bolu1234/article/details/51867099)
接上一篇shiro框架—关于用户登录和权限验证功能的实现步骤(三)
关于shiro在springboot的配置,共有四个基本配置文件主要的文件有四个ShiroConfig
、RetryLimitHashedCredentialsMatcher
、UserRealm
、MShiroFilterFactoryBean
。
因为上一篇仅仅写完了shiroconfig
类的配置,虽然在上一篇最下边,我已经附上了所有文件的下载链接,但是我觉得还是要写一下剩下的三个配置文件,写一下我对shiro配置的理解。下边是剩余三个配置文件,即RetryLimitHashedCredentialsMatcher
、UserRealm
、MShiroFilterFactoryBean
的配置详情。
1.UserRealm
类的配置内容
具体代码如下:
package microservice.fpzj.shiro;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import microservice.fpzj.core.models.Permission;
import microservice.fpzj.core.models.Role;
import microservice.fpzj.core.models.User;
import microservice.fpzj.service.jwt.UserService;
/**
* 验证用户登录
*
* @author Administrator
*/
@Component("userRealm")
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
public UserRealm() {
setName("UserRealm");
// 采用MD5加密
setCredentialsMatcher(new HashedCredentialsMatcher("md5"));
}
/**
*权限资源角色配置,如果在jsp里加入了shiro的标签配置,将会在访问那个jsp页
* 面的时候触发这个方法,这里先不考虑这个方法的逻辑。
**/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
//登录验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upt = (UsernamePasswordToken) token;
char[] passwords = upt.getPassword();
String pwd = new String(passwords);
if(!StringUtils.isEmpty(pwd)){ //如果密码不是空,表示是本地登录
User user = this.userService.findUserByUserName(upt.getUsername());
if(user == null){
throw new AuthenticationException("no_user");
}
boolean res =true;
for(Role r:user.getRoles()){
if(r.getSiteid()==2){
res = false;
break;
}
}
if(res){
throw new AuthenticationException("no_permission");
}
if(user!=null && !user.isDeleted()){
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
user, //用户对象
user.getPasswd(), //密码
ByteSource.Util.bytes(user.getUserid()+user.getUsername()),//salt=username+salt
getName()); //realm name
return info;
}
return null;//getAuthenticationInfoMethod(upt.getUsername());
}else{
/**
*如果密码是空,表示是远程登录,从username位置获得token,
*暂时不考虑这种情况,这里只走上边if里边
**/
String username = userService.getUsernameFromToken(upt.getUsername());
return getAuthenticationInfoMethod(username);
}
}
public AuthenticationInfo getAuthenticationInfoMethod(String username){
User user = userService.findUserByUserName(username);
if(user == null){
throw new AuthenticationException("no_user");
}
if(user != null){
boolean res =true;
for(Role r:user.getRoles()){
if(r.getSiteid()==2){
res = false;
break;
}
}
if(res){
throw new AuthenticationException("no_permission");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
user, //用户对象
user.getPasswd(), //密码
ByteSource.Util.bytes(user.getUserid()+user.getUsername()),//salt=username+salt
getName()); //realm name
return info;
}
return null;
}
}
- 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
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
以上主要看登录验证方法doGetAuthenticationInfo
在该方法里,通过从自己写的userService
接口,先根据用户名查询出用户对象,即调用findUserByUserName
方法,获取用户对象下的角色roles
,通过判断用户下的角色集合中,是否含有siteId
为2的,如果含有siteId
为2的,则让通过登录,如果没有当前siteId
的角色,则登录失败,站点siteId
表示系统类别,2在我们多系统里为业务系统,还有门户系统、后台系统等。
siteId
我在第一篇里已经写了,shiro框架—关于用户登录和权限验证功能的实现步骤(一)
2.RetryLimitHashedCredentialsMatcher
类的配置内容
具体代码如下:
package microservice.fpzj.shiro;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import microservice.fpzj.core.models.User;
import microservice.fpzj.service.jwt.UserService;
/**
*
* @author ZML
*
*/
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
@Autowired
private UserService userService;
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
if(token instanceof UsernamePasswordToken){
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
User user = userService.findUserByUserName(userToken.getUsername());
char[] passwordchars =userToken.getPassword();
String password = new String(passwordchars);
if("".equals(password)){ //token字符串登录
boolean flg = userService.verifyToken(userToken.getUsername());
return flg;
}else{ //账号密码登录
boolean res = userService.passwordVerify(password,user.getPasswd(),user.getUserid()+user.getUsername());
return res;
}
}
return false;
}
}
- 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
上一篇已经说过,RetryLimitHashedCredentialsMatcher
类,继承于HashedCredentialsMatcher
类,对于HashedCredentialsMatcher
类,你如果往上找,就会发现,其实就是实现了CredentialsMatcher
接口,最后又在shiroConfig
配置文件中,将该类注入到我们的spring
容器中供UserRealm
调用,加入到shiro
框架里,这样就可以扩展我们自定义的这个RetryLimitHashedCredentialsMatcher
类,该类的功能主要是将用户输入的密码与查询到的密码进行比较,也就是密码比较器。
上边方法doCredentialsMatch
的逻辑里,有verifyToken
这样的方法,其实这个是针对于那种没有账号密码,只有token
字符串的密钥登录的方式。这里先不说这一种。
userService
里是我们自己提前写好的一系列针对于用户验证的接口,因为shiro
不会去维护用户、权限、角色这些东西,需要我们自己维护,关于建表的那些步骤,我在第一篇里已经写了,shiro框架—关于用户登录和权限验证功能的实现步骤(一),比如这里的passwordVerify
方法 ,通过名字可以理解,是验证密码的接口,这些会在下一篇写出来。
3.MShiroFilterFactoryBean
类的配置内容
还记得在上一篇文章shiro框架—关于用户登录和权限验证功能的实现步骤(三) 里,提到过 MShiroFilterFactoryBean
继承于 ShiroFilterFactoryBean
类,是我们自己自定义的shiroFilter
,该类在shiroConfig
里注入到spring
容器中,另外,因为shiro
的拦截是对所有请求进行拦截,关于.js
、.css
、.html
、.png
等静态资源的请求,应该是不要拦截的,所以我们需要在自定义shiroFilter
里对这些静态资源进行过滤,对我们项目来说,这就是这个MShiroFilterFactoryBean
的存在的意义。
具体配置如下:
package microservice.fpzj.shiro;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.BeanInitializationException;
/**
* 自定义shiro过滤规则
* @author Administrator
*
*/
public class MShiroFilterFactoryBean extends ShiroFilterFactoryBean{
// 对ShiroFilter来说,需要直接忽略的请求
private Set<String> ignoreExt;
public MShiroFilterFactoryBean() {
super();
ignoreExt = new HashSet<>();
ignoreExt.add(".jsp");
// ignoreExt.add(".png");
}
@Override
protected AbstractShiroFilter createInstance() throws Exception {
org.apache.shiro.mgt.SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
FilterChainManager manager = createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new MSpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
private final class MSpringShiroFilter extends AbstractShiroFilter {
protected MSpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
super();
if (webSecurityManager == null) {
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
}
setSecurityManager(webSecurityManager);
if (resolver != null) {
setFilterChainResolver(resolver);
}
}
@Override
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
String str = request.getRequestURI().toLowerCase();
// 因为ShiroFilter 拦截所有请求(在上面我们配置了urlPattern 为 * ,当然你也可以在那里精确的添加要处理的路径,这样就不需要这个类了),而在每次请求里面都做了session的读取和更新访问时间等操作,这样在集群部署session共享的情况下,数量级的加大了处理量负载。
// 所以我们这里将一些能忽略的请求忽略掉。
// 当然如果你的集群系统使用了动静分离处理,静态资料的请求不会到Filter这个层面,便可以忽略。
boolean flag = true;
int idx = 0;
if(( idx = str.indexOf(".")) > 0 ){
str = str.substring(idx);
/**
*如果不是jsp则为false,即除了.jsp,其他的都不需要拦截
**/
if(!ignoreExt.contains(str.toLowerCase())){
flag = false;
}
}
if(flag){
super.doFilterInternal(servletRequest, servletResponse, chain);
}else{
chain.doFilter(servletRequest, servletResponse);
}
}
}
}
- 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
通过以上的配置,就会将所有不是.jsp
的又带有.
资源全部放开,即,没有登录的情况下,可以访问到,其他带有.
的资源则全部被shiro拦截。
4.注意点
以上的配置文件中,你会看到有很多的userService
中的方法,我没有贴出来,后边我也都会贴出来,但是,其实这样的逻辑基本都是数据库查询的逻辑。
比如:
下面的这个是根据用户名获取用户对象的方法,因为用户名是唯一的,这个应该很容易写出来,就是根据用户名字段去匹配用户表的一条对象记录。
User user = userService.findUserByUserName(upt.getUsername())
- 1
下面的这个是验证账号密码的方法。
boolean flag = userService.passwordVerify(password,user.getPasswd(),user.getUserid()+user.getUsername())
- 1
下面的这个是根据token字符串获取用户名,token是一种jwt字符串,是一种很长的字符串,这里主要用于那种没有用户名,只有一个令牌token字符串,通过这个token字符串,来到后台验证的时候,需要通过这个字符串转换成用户名,然后再根据用户名,调用上边的方法获取用户对象。这里先不考虑这种的登录,只考虑正常的那种账号密码的登录方式。
String username = userService.getUsernameFromToken(upt.getUsername())
- 1
下面的这个是验证客户端传入的token字符串是否有效的接口。同样这里不考虑这种的登录方式。
boolean flag = userService.verifyToken(userToken.getUsername())
- 1