shiro简介:
shiro是apache提供的一个强大易用的Java安全框架,用于身份验证、授权、密码学和会话管理。点我下载源码。
开发环境及技术:
1、mysql - 5.7.21
2、navicat(mysql客户端管理工具)
3、eclipse
4、jdk9
5、tomcat 8.5
6、spring & springmvc
7、mybatis 3
8、shiro
9、maven
现在直接开始建项目,从项目中具体讲解shiro的使用,项目虽简单,却五脏俱全。
欢迎大家关注我的公众号 javawebkf,目前正在慢慢地将简书文章搬到公众号,以后简书和公众号文章将同步更新,且简书上的付费文章在公众号上将免费。
一、数据库设计:
数据库有三张表,分别是tb_user用户表,tb_role角色表,tb_permission权限表。
1、tb_user
图片发自简书App
设置外键rid关联tb_role表
图片发自简书App
2、tb_role
图片发自简书App
3、tb_permission
图片发自简书App
设置外键关联tb_role表
图片发自简书App
二、项目环境搭建:
1、新建maven web app,结构如下:
图片发自简书App
2、接下来就是添加依赖:
junit
junit
4.12
test
javax.servlet
javax.servlet-api
3.1.0
javax.servlet.jsp
javax.servlet.jsp-api
2.3.1
javax.servlet
jstl
1.2
org.springframework
spring-core
4.1.7.RELEASE
org.springframework
spring-beans
4.1.7.RELEASE
org.springframework
spring-tx
4.1.7.RELEASE
org.springframework
spring-context
4.1.7.RELEASE
org.springframework
spring-context-support
4.1.7.RELEASE
org.springframework
spring-web
4.1.7.RELEASE
org.springframework
spring-webmvc
4.1.7.RELEASE
org.springframework
spring-aop
4.1.7.RELEASE
org.springframework
spring-aspects
4.1.7.RELEASE
org.springframework
spring-jdbc
4.1.7.RELEASE
org.springframework
spring-test
4.3.14.RELEASE
test
commons-collections
commons-collections
3.2
org.mybatis
mybatis-spring
1.2.3
org.mybatis
mybatis
3.3.0
log4j
log4j
1.2.17
org.slf4j
slf4j-log4j12
1.7.12
mysql
mysql-connector-java
5.1.37
com.mchange
c3p0
0.9.5.2
org.apache.shiro
shiro-core
1.2.4
org.apache.shiro
shiro-web
1.2.4
org.apache.shiro
shiro-spring
1.2.4
依赖对应的作用在代码中有简要的注释。
3、完成配置文件:
在spring文件夹下有:
spring-dao.xml,
spring-mvc.xml,
spring-service.xml,
spring-shiro.xml
①spring-dao.xml
location="classpath:jdbc.properties" />
class="com.mchange.v2.c3p0.ComboPooledDataSource">
class="org.mybatis.spring.SqlSessionFactoryBean">
value="classpath:mappers/*.xml" />
value="classpath:mybatis-config.xml" />
value="sqlSessionFactory">
②spring-service.xml
③spring-mvc.xml
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" />
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
注意:开启shiro注解的配置要写在spring-mvc.xml中,具体原因我也不太清楚,有知道的大佬还请留言指教哦!
④spring-shiro.xml
/login=anon
/admin/**=authc,roles[admin]
以下配置文件在resources根目录:
⑤jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///#?useUnicode=true&characterEncoding=utf8
jdbc.username=#
jdbc.password=#
⑥log4j.properties
log4j.rootLogger=DEBUG, Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
⑦mybatis-config.xml
/p>
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
web.xml在WEB-INF目录下:
⑧web.xml
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true">
shiroweb
index.jsp
contextConfigLocation
classpath:spring/spring-*.xml
springMVC
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring/spring-mvc.xml
1
true
springMVC
/
org.springframework.web.context.ContextLoaderListener
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
shiroFilter
/*
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
true
encoding
UTF-8
encodingFilter
/*
至此已完成所有配置,所有的配置文件核心代码都写了注释。
三、项目功能实现
登录验证:
项目功能描述:
在数据库中有两个用户,一个tom,角色为admin,对应的权限有create,delete,query和update,另一个用户cat,角色为guest,权限只有create和query。
1、首先新建User实体类(set、get方法略):
User.java
public class User {
private Integer uid;
private String userName;
private String password;
}
2、dao层的开发
UserDao.java
public interface UserDao {
/**
* 根据用户名查询用户
* @param userName
* @return
*/
public User getByUserName(String userName);
/**
* 根据用户名查询角色
* @param userName
* @return
*/
public Set getRoles(String userName);
/**
* 根据用户名查询权限
* @param userName
* @return
*/
public Set getPermissions(String userName);
}
UserDao.xml
/p>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select *
from tb_user
where user_name=#{userName}
select r.role_name
from tb_user u,tb_role r
where u.rid=r.rid
and u.user_name=#{userName}
select p.permission_name
from tb_user u,tb_role r,tb_permission p
where u.rid=r.rid
and p.rid=r.rid
and u.user_name=#{userName}
注意:这里并没有role和permission对应的实体类,也可以新建其对应的实体类,然后把他们设置为User的成员变量,用List集合装载。但是这样做更麻烦一点,因为等下shiro要用到的就是Set集合,如果是List等下还需做转换。
3、dao层测试
写到这可以做一下junit测试,由于篇幅原因,此处不再赘述。
4、service层开发
由于并没有增加逻辑,只是简单调用dao层,所以不再说明。
5、自定义realm
MyRealm.java
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 为登录用户授予权限和角色
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(userService.getRoles(userName));
authorizationInfo.setStringPermissions(userService.getPermissions(userName));
return authorizationInfo;
}
/**
* 验证当前登录用户
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = (String) token.getPrincipal();
User user = userService.getByUserName(userName);
//使用md5加密
//当前realm对象的name
String realmName = getName();
//盐值
ByteSource credenttialsSalt = ByteSource.Util.bytes(user.getUserName());
//封装用户信息,构建AuthenticationInfo对象并返回
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(),
credenttialsSalt, realmName);
return authcInfo;
//使用md5加密
//不加密
/*
* if (user != null) {
* AuthenticationInfo authcInfo = new
* SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), "xx");
* return authcInfo;
* } else {
* return null;
* }
*/
//不加密
}
这个类就验证登录和为用户授权两个方法,代码中已有注释说明。若不使用MD5加密,则把我写了MD5加密注释之间那段代码注释掉,把下面注释放开就行;若要使用MD5加密,数据库中的密码也得是加密后的密码可以通过下面的方法获取明文密码对应的密文密码。
获取密文的方法:
public static void main(String[] args) {
String hashAlgorithName = "MD5";
String password = "5678";
int hashIterations = 1024;
ByteSource credentialsSalt = ByteSource.Util.bytes("cat");
Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
System.out.println(obj);
}
6、用户登录的Controller
@Controller
@RequestMapping("/user")
public class UserController {
/**
*
* @param user
* @param request
* @return
*/
@RequestMapping("/login")
public String login(User user,HttpServletRequest request){
//获取当前登录用户
Subject subject=SecurityUtils.getSubject();
//封装表单中提交的用户名和密码
UsernamePasswordToken token=new UsernamePasswordToken(user.getUserName(), user.getPassword());
try{
//调用login方法,传入封装好的token
subject.login(token);
//登录成功跳转success.jsp
return "redirect:/success.jsp";
}catch(Exception e){
e.printStackTrace();
//登录失败就重新登录
request.setAttribute("errorMsg", "登录失败");
return "login";
}
}
}
7、login.jsp
pageEncoding="UTF-8"%>
Insert title hereuserName:
password:
${errorMsg }
过程梳理:
在controller中获取到前端表单输入的用户名和密码,封装到token中,然后调用subject的login的方法,传入这个token,这个token就会把数据带到realm中,realm中再调service层查询,根据前端token中携带的用户名去查询数据库中记录,进行密码比对。
这里解释spring-shiro.xml中/login=anon的作用,这行代码的意思就是不拦截登录方法,可以匿名访问。
以上就是验证登录的整个流程。
授权:
需求描述:
1、指定角色:
AdminController只有具有admin角色(tom)才能访问;
GuestController只有具有guest角色(cat)才能访问;
2、指定权限:
PermissionController只有具有create权限(tom和cat)的用户才能访问;
涉及知识点:
1、在spring-shiro.xml中进行授权验证
2、注解方式授权验证
3、jsp中授权验证
4、多级路由匹配规则
正式开始:
1、AdminController.java
@Controller
@RequestMapping("/admin")
public class AdminController {
/**
*
* @param user
* @param request
* @return
*/
@RequestMapping("/test")
public String adminTest() {
//Subject subject = SecurityUtils.getSubject();
//if(subject.hasRole("admin")) {
return "admin";
//}else {
// return "login";
//}
}
}
这个授权验证写在spring-shiro.xml中:/admin/**=authc,roles[admin],这样就表示要有admin这个角色的用户才能访问。同时这个也是双重路由,/admin/**就表示拦截admin开头的路由。
2、GuestController.java
public class GuestController {
@RequiresRoles("guest")
@RequestMapping("/guest")
public String guestTest() {
return "guest";
}
}
这个是使用注解方式进行权限验证的,@RequiresRoles("guest")就表示要有guest这个角色才能访问这个路由。
3、PermissionsController.java
@Controller
public class PermissionsController {
/**
*
* @param user
* @param request
* @return
*/
@RequestMapping("/permissions")
public String permissionTest(){
Subject subject = SecurityUtils.getSubject();
if(subject.isPermitted("create")) {
return "permission";
}else {
return "login";
}
}
}
这个是直接在Controller中获取到登录用户,然后用Subject对象的isPermitted方法就行判断。subject.isPermitted("create")用来判断登录的用户是否有create权限,有就true,没有就是false。
4、接下来看看前端页面:
①success.jsp
登录成功跳转到这个页面
Insert title here欢迎你!
这里定义了三个链接,分别指向上面那三个controller。
②admin.jsp
pageEncoding="UTF-8"%>
Insert title here欢迎有admin角色的你!
③guest.jsp
pageEncoding="utf-8"%>
Insert title here欢迎guest!(只有guest角色登录进来才能显示这段话)
欢迎admin!(只有admin角色登录进来才能显示这段话)
这里就用到了jsp标签形式判断角色,xxx就是用来判断当前用户有没有#角色,有#角色才会显示xxx内容。
注意:使用shiro的jsp标签要在jsp页面中添加
④permission.jsp
pageEncoding="UTF-8"%>
Insert title here欢迎有你!
只有拥有delete权限的人登录进来才能显示这段话.
这里用到了xxx来判断权限,只有具有#权限的用户才会显示xxx内容。
⑤unauthor.jsp
pageEncoding="utf-8"%>
Insert title here对不起,你没有操作权限!
权限认证失败时跳转到此页面,因为在spring-shiro.xml中配置过。
四、项目测试
1、用tom登录
分析:tom具有admin角色和所有权限,所以success.jsp页面的前两个链接可以访问,且permission.jsp的标签里面的话会显示。第三个链接不能访问,会抛出异常,因为需要guest角色才能访问。
结果:
图片发自简书App
图片发自简书App
图片发自简书App
图片发自简书App
图片发自简书App
2、用cat登录
分析:cat具有guest角色和create 以及query权限,所以success.jsp的第一个链接不能访问,会跳转到unauthor.jsp,第二个链接可以访问,但是不能显示标签里面的话,第三个链接可以访问,且能显示里面的话。
结果:
图片发自简书App
图片发自简书App
图片发自简书App
所有结果符合预期,测试通过!
五、知识点补充
1、url匹配规则:
/admin?=authc
表示admin1需要认证,admin2也需要,但是admin不一定,因为问号表示单个字符
/admin*=authc
表示admin1需要认证,admin21需要,admin也需要,*号表示一个或多个字符
/admin/**=authc
可以匹配多路径,比如admin/a/b
2、
Subject对象除了isPermitted("#")判断是否拥有#权限,还有hasRole("#")判断是否有#角色,hasRoles(Arrays.asList("role1","role2"))来判断是否有role1和role2角色。
3、其他注释:
@RequiresAuthentication
表示要验证通过才能被访问
@RequiresGuest
表示之前session中没有被验证过才能访问
@RequiresPermissions("create")
表示有create权限才能访问
@RequiresRoles("admin")
只有admin这个角色才能访问
@RequiresUser
指定用户才能访问
总结:
shiro提供了认证登录,授权,密码加密等功能,方便易用。授权可以在spring-shiro中定义过滤链,可以使用注释,也可以在controller 方法中用Subject对象的方法判断,还可以在jsp中使用标签。
以上内容是个人学习笔记整理,如有错误,欢迎批评指正!