既然要做,就做的细致一点,对得起自己!
shiro使用前配置:
1.pom文件中引入shiro-spring集成的jar包。
2.web.xml中配置shiro的过滤器,拦截所有请求。
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<init-param>
<!--这个参数作用是将shiro交给Spring管理-->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<!--不配置也默认为request-->
<!--这个元素有四个可能的值:即REQUEST,FORWARD,INCLUDE和ERROR,
可以在一个<filter-mapping>元素中加入任意数目的<dispatcher>,使得filter将会作用于直接
从客户端过来的request,通过forward过来的request,通过include过来的request和通过
<error-page>过来的request。-->
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
3.书写shiro.xml配置文件。配置文件中目前的主要信息包括
<bean id="shiroFilter" class="org.apache.shiro.spring.web.一个shiroFactoryBeab">
<!--必须属性,securityManager,它是整个shiro的生存环境-->
<property name="securityManager" ref="securityManager"/>
<!--登录是对应的url-->
<property name="loginUrl" value="portal/login"/>
<!--认证成功,但是没有对应的权限时候对应的url-->
<property name="unauthorizedUrl" value="/403"/>
<!--authc是需要认证的,anon是不需要认证的-->
<property name="filterChainDefinitions">
<value>
/protal/index = authc
/portal/login = anon
/403 = anon
/portal/show = roles["admin"]
/** = authc
</value>
</property>
</bean>
<!--引入securityManager-->
<bean id="securityManager"class="org.apache.shiro.web.mgt.DefaultWebSecurityManage>
<!--自定义realm,realm是我们自己继承shiro重写的,可以实现自定义的认证和授权-->
<property name="customRealm" ref="customRealm"/>
</bean>
<bean id="customRealm" class="自定义realm的类"/>
4.在Spring配置文件中导入shiro.xml.<import resourc="shiro.xml"/>
5.实现自定义Realm,继承AuthorizingRealm,这里要注意几点,实际业务登录场景中,可能出现的情况为
第一:用户没有注册,用户不存在,代码中对应的处理方式为抛出UnKnownAccountException,提示用户不存在/未注册。
第二:用户名和密码不匹配,代码中对应的处理方式为抛出IncorrectCredentialsException,提示用户名或者密码错误,不会具体提示密码不对,为了安全考虑。
第三:当用户输入密码错误次数过多,应该在一段时间内拒绝该用户的登录请求,这个目前还没有实现,后期完善。
第四:当用户登录-->被拒绝-->登录-->被拒绝这种情况短时间内多次发生,为了安全考虑,此时应该将该用户锁住,代码中对应的处理方式为抛出LockedAccountException,提示账号被锁。要求用户进行更为安全的账号申诉流程。
PS:第三和第四步是为了防止暴力破解以及大量用户短时间请求对服务器造成的压力!
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.根据authenticationToken获取到主体中的用户,并转换为UsernamePasswordToken
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//2.利用UsernamePasswordToken获取username,根据username去数据库查找数据
String username = token.getUsername();
//3.为实体Model设置属性,
// 这里为什么不采用获取到username,去数据库查询是否和现在的password是否一样呢?
// 是因为个人觉得使用实体查询会更便捷,数据库查询直接返回有或者没有即可,如果是上述的操作,会很繁琐
User authUser = new User();
authUser.setUserName(username);
//这里就是实际项目中数据库查询的操作,返回一个User对象
authUser = getUserByInfo(authUser);
//根据返回的User对象进行判断
if (authUser == null){
throw new UnknownAccountException("用户名不存在");
}
if ("SD".equals(authUser.getState())){
throw new LockedAccountException("账号被锁定,请联系管理员!");
}
//这里一定一定要注意,info中的第二个参数一定要是查询之后数据库里password对应的值,而不是token.getPassWord()的值
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(authUser,authUser.getPassWord(),getName());
return info;
}
2019-04-20更新:这里一定一定要注意,info中的第二个参数一定要是查询之后数据库里password对应的值,而不是token.getPassWord()的值,以下为正确写法:
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(authUser,authUser.getPassWord(),getName());
以下为错误示例
你可能会在前面获取过你登录时提交的密码:
String password = String.valueOf(token.getPassWord());
然后你在这里可能会写成这样,认证的时候,会报空指针或者,credentials不匹配的错误
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(authUser,password,getName());
原因在这篇文章的最后有介绍。
完成基本配置,开始登录流程,代码部分,简单的实现。
@requestMapping(value = "loginAuth")
public String loginAuth(String username, String password) {
//1.获得当前用户,相当于进入shiro系统的钥匙
Subject subject = SecurityUtils.getSubject();
//2.要验证的信息,相当于同shiro交易的东西
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
//3.进入自定义的realm中进行认证操作,
subject.login(token);
return "redirect:/portal/index";
} catch (Exception e) {
System.out.println(e.getMessage());
return "redirect:/portal/login";
}
}
其中subject.login(token)就会去自定义的Realm中进行用户的认证操作。完成认证,进入系统。
重要说明:
1.这里shiro是和SpringMVC、Spring、Mybatis集成的。此时看博文的你应该也是shiro集成在Spring大家族里的。对于shiro配置文件中loginUrl的value配置,网上90%的帖子都会告诉你,是web项目下login页面地址,如果不配置,shiro会默认找名字为login页面的地址。切记:如果你和SpringMvc集成了,那么你的web.xml中关于SpringMVC的url-pattern应该是/或者*.jsp或者*.html或者*.do 等,千万别傻傻的真把loginUrl配置为页面的名字,如果这样做,90%会无法跳转,而且你还会看到一条No request Mapping with....的错误。既然你的SpringMVC拦截了所有请求,这里配置的肯定是你要跳转到登录页面的url,而不是真的登录页面的名字。shiro中所有拦截的都是你请求的url,而不是你的页面名字。这一点很多博文和帖子都会告诉你是什么什么页面名字。
2.关于shiro中successUrl的配置,确实不需要配置,因为配置了也没啥用,这一点网上的答案倒是很靠谱。验证通过之后shiro会默认走你的上一次请求的url,我在第一次登录验证时,方法返回为void,于是shiro是直接返回我的请求url,被SpringMVC拦截到,去找相应的视图(肯定找不到),报错。网上给出的解决的答案又是清一色的重写shiro对于的方法,将自己的url设置进去。我就纳闷了,就为了一个跳转,大家都愿意这么费劲?第二次登录验证时,我的解决方法是直接利用ModelAndView跳转到对应的视图,这就解决了不需要重写方法再跳转,但是有一个问题,上面也说了,shiro验证通过返回的url是上次的url,出现的情况是,我的视图虽然跳转了,但是我的url没有变。这就是一个很大的问题了,如果我在跳转之后的页面做了什么业务操作,对服务器发起请求,平时的做法都是${baseUrl}/具体的controller/具体方法,这里的baseUrl就是我当前的url地址,这个地址其实是错误的,因为他没有改变过,很有可能SpringMVC找不到对应的controller,即使找到了,那么里面的业务逻辑也不是我们实际上要执行的业务逻辑。所以第三次我采取的是在认证通过之后,直接redirect,这样既保证了页面的正确跳转,也保证了对应url的改变。
实际业务的两种不同的场景:
1.如淘宝,天猫这类的电商类平台,不需要用户进入系统就登录,只有用户触发了实际业务操作(加入购物车,收藏)需要同用户信息关联起来的时候,才提示用户登录。基本实现方式为,进入系统。不会拦截(anon),当用户触发条件时,放在代码里说就是发起了对具有authc的url的访问时,那么shiro就会将其定位到配置的loginUrl的value(具体的action/cntroller)中,验证通过后,带着用户信息返回到上个页面。完成用户的操作。
2.如后台管理系统这类的,则需要进入系统即验证用户的合法性,在访问系统一开始,就对其进行拦截,用户完成认证之后再进入系统首页或者系统的导航菜单等。
以上写的有点不简洁,只是希望此时看博文的你,能够用的明白,而不是照着网上的帖子复制、粘贴,最后还漏洞百出,把自己搞得模棱两可!
切勿眼高手低!