Shiro认证
导入pom依赖
<shiro.version>1.2.5</shiro.version>
1 <!--shiro--> 2 <dependency> 3 <groupId>org.apache.shiro</groupId> 4 <artifactId>shiro-core</artifactId> 5 <version>${shiro.version}</version> 6 </dependency> 7 8 <dependency> 9 <groupId>org.apache.shiro</groupId> 10 <artifactId>shiro-web</artifactId> 11 <version>${shiro.version}</version> 12 </dependency> 13 14 <dependency> 15 <groupId>org.apache.shiro</groupId> 16 <artifactId>shiro-spring</artifactId> 17 <version>${shiro.version}</version> 18 </dependency>
配置web.xml
1 <!-- shiro过滤器定义 --> 2 <filter> 3 <filter-name>shiroFilter</filter-name> 4 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 5 <init-param> 6 <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 --> 7 <param-name>targetFilterLifecycle</param-name> 8 <param-value>true</param-value> 9 </init-param> 10 </filter> 11 <filter-mapping> 12 <filter-name>shiroFilter</filter-name> 13 <url-pattern>/*</url-pattern> 14 </filter-mapping>
通过逆向将数据库表生成对应的model、mapper类
这里就不一一展示生成的结果了。
主要看一下需要用到的ShiroUserMapper和ShiroUserMapper.xml
ShiroUserMapper
1 package com.yuan.mapper; 2 3 import com.yuan.model.ShiroUser; 4 import org.apache.ibatis.annotations.Param; 5 import org.springframework.stereotype.Repository; 6 7 @Repository 8 public interface ShiroUserMapper { 9 int deleteByPrimaryKey(Integer userid); 10 11 int insert(ShiroUser record); 12 13 int insertSelective(ShiroUser record); 14 15 ShiroUser selectByPrimaryKey(Integer userid); 16 17 int updateByPrimaryKeySelective(ShiroUser record); 18 19 int updateByPrimaryKey(ShiroUser record); 20 21 ShiroUser queryByName(@Param("uname")String uname); 22 23 }
ShiroUserMapper.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > 3 <mapper namespace="com.yuan.mapper.ShiroUserMapper" > 4 <resultMap id="BaseResultMap" type="com.yuan.model.ShiroUser" > 5 <constructor > 6 <idArg column="userid" jdbcType="INTEGER" javaType="java.lang.Integer" /> 7 <arg column="username" jdbcType="VARCHAR" javaType="java.lang.String" /> 8 <arg column="PASSWORD" jdbcType="VARCHAR" javaType="java.lang.String" /> 9 <arg column="salt" jdbcType="VARCHAR" javaType="java.lang.String" /> 10 <arg column="createdate" jdbcType="TIMESTAMP" javaType="java.util.Date" /> 11 </constructor> 12 </resultMap> 13 <sql id="Base_Column_List" > 14 userid, username, PASSWORD, salt, createdate 15 </sql> 16 <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > 17 select 18 <include refid="Base_Column_List" /> 19 from t_shiro_user 20 where userid = #{userid,jdbcType=INTEGER} 21 </select> 22 <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" > 23 delete from t_shiro_user 24 where userid = #{userid,jdbcType=INTEGER} 25 </delete> 26 <insert id="insert" parameterType="com.yuan.model.ShiroUser" > 27 insert into t_shiro_user (userid, username, PASSWORD, 28 salt, createdate) 29 values (#{userid,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, 30 #{salt,jdbcType=VARCHAR}, #{createdate,jdbcType=TIMESTAMP}) 31 </insert> 32 <insert id="insertSelective" parameterType="com.yuan.model.ShiroUser" > 33 insert into t_shiro_user 34 <trim prefix="(" suffix=")" suffixOverrides="," > 35 <if test="userid != null" > 36 userid, 37 </if> 38 <if test="username != null" > 39 username, 40 </if> 41 <if test="password != null" > 42 PASSWORD, 43 </if> 44 <if test="salt != null" > 45 salt, 46 </if> 47 <if test="createdate != null" > 48 createdate, 49 </if> 50 </trim> 51 <trim prefix="values (" suffix=")" suffixOverrides="," > 52 <if test="userid != null" > 53 #{userid,jdbcType=INTEGER}, 54 </if> 55 <if test="username != null" > 56 #{username,jdbcType=VARCHAR}, 57 </if> 58 <if test="password != null" > 59 #{password,jdbcType=VARCHAR}, 60 </if> 61 <if test="salt != null" > 62 #{salt,jdbcType=VARCHAR}, 63 </if> 64 <if test="createdate != null" > 65 #{createdate,jdbcType=TIMESTAMP}, 66 </if> 67 </trim> 68 </insert> 69 <update id="updateByPrimaryKeySelective" parameterType="com.yuan.model.ShiroUser" > 70 update t_shiro_user 71 <set > 72 <if test="username != null" > 73 username = #{username,jdbcType=VARCHAR}, 74 </if> 75 <if test="password != null" > 76 PASSWORD = #{password,jdbcType=VARCHAR}, 77 </if> 78 <if test="salt != null" > 79 salt = #{salt,jdbcType=VARCHAR}, 80 </if> 81 <if test="createdate != null" > 82 createdate = #{createdate,jdbcType=TIMESTAMP}, 83 </if> 84 </set> 85 where userid = #{userid,jdbcType=INTEGER} 86 </update> 87 <update id="updateByPrimaryKey" parameterType="com.yuan.model.ShiroUser" > 88 update t_shiro_user 89 set username = #{username,jdbcType=VARCHAR}, 90 PASSWORD = #{password,jdbcType=VARCHAR}, 91 salt = #{salt,jdbcType=VARCHAR}, 92 createdate = #{createdate,jdbcType=TIMESTAMP} 93 where userid = #{userid,jdbcType=INTEGER} 94 </update> 95 96 <select id="queryByName" resultType="com.yuan.model.ShiroUser" parameterType="java.lang.String" > 97 select 98 <include refid="Base_Column_List" /> 99 from t_shiro_user 100 where username = #{uname} 101 </select> 102 103 104 </mapper>
Service层
1 package com.yuan.service; 2 3 import com.yuan.model.ShiroUser; 4 import org.apache.ibatis.annotations.Param; 5 6 public interface ShiroUserService { 7 8 ShiroUser queryByName(@Param("uname")String uname); 9 10 int insert(ShiroUser record); 11 12 }
Service实现类
package com.yuan.service.impl; import com.yuan.mapper.ShiroUserMapper; import com.yuan.model.ShiroUser; import com.yuan.service.ShiroUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("shiroUserService") public class ShiroUserServiceImpl implements ShiroUserService { @Autowired private ShiroUserMapper shiroUserMapper; @Override public ShiroUser queryByName(String uname) { return shiroUserMapper.queryByName(uname); } @Override public int insert(ShiroUser record) { return shiroUserMapper.insert(record); } }
MyRealm
1 package com.yuan.shiro; 2 3 import com.yuan.mapper.ShiroUserMapper; 4 import com.yuan.model.ShiroUser; 5 import com.yuan.service.ShiroUserService; 6 import lombok.ToString; 7 import org.apache.shiro.authc.AuthenticationException; 8 import org.apache.shiro.authc.AuthenticationInfo; 9 import org.apache.shiro.authc.AuthenticationToken; 10 import org.apache.shiro.authc.SimpleAuthenticationInfo; 11 import org.apache.shiro.authz.AuthorizationInfo; 12 import org.apache.shiro.realm.AuthorizingRealm; 13 import org.apache.shiro.subject.PrincipalCollection; 14 import org.apache.shiro.util.ByteSource; 15 import org.springframework.stereotype.Component; 16 17 18 /* 19 替换掉.ini的文件,所有用户的身份数据源 20 */ 21 public class MyRealm extends AuthorizingRealm { 22 private ShiroUserService shiroUserService; 23 24 public ShiroUserService getShiroUserService() { 25 return shiroUserService; 26 } 27 28 public void setShiroUserService(ShiroUserService shiroUserService) { 29 this.shiroUserService = shiroUserService; 30 } 31 32 /* 33 授权的方法 34 */ 35 @Override 36 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { 37 return null; 38 } 39 40 /* 41 身份认证的方法 42 */ 43 @Override 44 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { 45 String uname = authenticationToken.getPrincipal().toString(); 46 String pwd = authenticationToken.getCredentials().toString(); 47 ShiroUser shiroUser = shiroUserService.queryByName(uname); 48 49 AuthenticationInfo info = new SimpleAuthenticationInfo( 50 shiroUser.getUsername(), 51 shiroUser.getPassword(), 52 ByteSource.Util.bytes(shiroUser.getSalt()), 53 this.getName() 54 ); 55 56 return info; 57 } 58 59 }
applicationContext-shiro.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 6 <!--配置自定义的Realm--> 7 <bean id="shiroRealm" class="com.yuan.shiro.MyRealm"> 8 <property name="shiroUserService" ref="shiroUserService" /> 9 <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 --> 10 <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 --> 11 <!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 --> 12 <!--以下三个配置告诉shiro将如何对用户传来的明文密码进行加密--> 13 <property name="credentialsMatcher"> 14 <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> 15 <!--指定hash算法为MD5--> 16 <property name="hashAlgorithmName" value="md5"/> 17 <!--指定散列次数为1024次--> 18 <property name="hashIterations" value="1024"/> 19 <!--true指定Hash散列值使用Hex加密存. false表明hash散列值用用Base64-encoded存储--> 20 <property name="storedCredentialsHexEncoded" value="true"/> 21 </bean> 22 </property> 23 </bean> 24 25 <!--注册安全管理器--> 26 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 27 <property name="realm" ref="shiroRealm" /> 28 </bean> 29 30 <!--Shiro核心过滤器--> 31 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 32 <!-- Shiro的核心安全接口,这个属性是必须的 --> 33 <property name="securityManager" ref="securityManager" /> 34 <!-- 身份验证失败,跳转到登录页面 --> 35 <property name="loginUrl" value="/login"/> 36 <!-- 身份验证成功,跳转到指定页面 --> 37 <!--<property name="successUrl" value="/index.jsp"/>--> 38 <!-- 权限验证失败,跳转到指定页面 --> 39 <property name="unauthorizedUrl" value="/unauthorized.jsp"/> 40 <!-- Shiro连接约束配置,即过滤链的定义 --> 41 <property name="filterChainDefinitions"> 42 <value> 43 <!-- 44 注:anon,authcBasic,auchc,user是认证过滤器 45 perms,roles,ssl,rest,port是授权过滤器 46 --> 47 <!--anon 表示匿名访问,不需要认证以及授权--> 48 <!--authc表示需要认证 没有进行身份认证是不能进行访问的--> 49 <!--roles[admin]表示角色认证,必须是拥有admin角色的用户才行--> 50 /user/login=anon 51 /user/updatePwd.jsp=authc 52 /admin/*.jsp=roles[admin] 53 /user/teacher.jsp=perms["user:update"] 54 <!-- /css/** = anon 55 /images/** = anon 56 /js/** = anon 57 / = anon 58 /user/logout = logout 59 /user/** = anon 60 /userInfo/** = authc 61 /dict/** = authc 62 /console/** = roles[admin] 63 /** = anon--> 64 </value> 65 </property> 66 </bean> 67 68 <!-- Shiro生命周期,保证实现了Shiro内部lifecycle函数的bean执行 --> 69 <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> 70 </beans>
ShiroUserController
1 package com.yuan.controller; 2 3 4 import com.yuan.model.ShiroUser; 5 import com.yuan.service.ShiroUserService; 6 import com.yuan.util.PasswordHelper; 7 import org.apache.shiro.SecurityUtils; 8 import org.apache.shiro.authc.AuthenticationException; 9 import org.apache.shiro.authc.UsernamePasswordToken; 10 import org.apache.shiro.subject.Subject; 11 import org.springframework.beans.factory.annotation.Autowired; 12 import org.springframework.stereotype.Controller; 13 import org.springframework.web.bind.annotation.RequestMapping; 14 15 import javax.servlet.http.HttpServletRequest; 16 import javax.servlet.http.HttpServletResponse; 17 18 @Controller 19 public class ShiroUserController { 20 21 @Autowired 22 private ShiroUserService shiroUserService; 23 24 25 /* 26 用户登录 27 */ 28 @RequestMapping("/login") 29 public String login(HttpServletRequest req){ 30 Subject subject = SecurityUtils.getSubject(); 31 String uname = req.getParameter("username"); 32 String pwd = req.getParameter("password"); 33 UsernamePasswordToken token = new UsernamePasswordToken(uname, pwd); 34 35 try { 36 // 这里会跳转到MyRealm中的认证方法 37 subject.login(token); 38 req.getSession().setAttribute("username", uname); 39 return "main"; 40 41 } catch (AuthenticationException e) { 42 req.setAttribute("message","用户名或密码错误!!!"); 43 return "login"; 44 } 45 46 } 47 48 /* 49 退出登录 50 */ 51 @RequestMapping("/logout") 52 public String logout(HttpServletRequest req) { 53 Subject subject = SecurityUtils.getSubject(); 54 subject.logout(); 55 return "redirect:/login.jsp"; 56 } 57 58 59 /* 60 用户注册 61 */ 62 @RequestMapping("/register") 63 public String register(HttpServletRequest req, HttpServletResponse resp){ 64 String uname = req.getParameter("username"); 65 String pwd = req.getParameter("password"); 66 String salt = PasswordHelper.createSalt(); 67 String credentials = PasswordHelper.createCredentials(pwd, salt); 68 69 ShiroUser shiroUser=new ShiroUser(); 70 shiroUser.setUsername(uname); 71 shiroUser.setPassword(credentials); 72 shiroUser.setSalt(salt); 73 int insert = shiroUserService.insert(shiroUser); 74 if(insert>0){ 75 req.setAttribute("message","注册成功"); 76 return "login"; 77 } 78 else{ 79 req.setAttribute("message","注册失败"); 80 return "register"; 81 } 82 } 83 84 85 86 }
盐加密的工具类
PasswordHelper
1 package com.yuan.util; 2 3 import org.apache.shiro.crypto.RandomNumberGenerator; 4 import org.apache.shiro.crypto.SecureRandomNumberGenerator; 5 import org.apache.shiro.crypto.hash.SimpleHash; 6 7 public class PasswordHelper { 8 9 /** 10 * 随机数生成器 11 */ 12 private static RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator(); 13 14 /** 15 * 指定hash算法为MD5 16 */ 17 private static final String hashAlgorithmName = "md5"; 18 19 /** 20 * 指定散列次数为1024次,即加密1024次 21 */ 22 private static final int hashIterations = 1024; 23 24 /** 25 * true指定Hash散列值使用Hex加密存. false表明hash散列值用用Base64-encoded存储 26 */ 27 private static final boolean storedCredentialsHexEncoded = true; 28 29 /** 30 * 获得加密用的盐 31 * 32 * @return 33 */ 34 public static String createSalt() { 35 return randomNumberGenerator.nextBytes().toHex(); 36 } 37 38 /** 39 * 获得加密后的凭证 40 * 41 * @param credentials 凭证(即密码) 42 * @param salt 盐 43 * @return 44 */ 45 public static String createCredentials(String credentials, String salt) { 46 SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, credentials, 47 salt, hashIterations); 48 return storedCredentialsHexEncoded ? simpleHash.toHex() : simpleHash.toBase64(); 49 } 50 51 52 /** 53 * 进行密码验证 54 * 55 * @param credentials 未加密的密码 56 * @param salt 盐 57 * @param encryptCredentials 加密后的密码 58 * @return 59 */ 60 public static boolean checkCredentials(String credentials, String salt, String encryptCredentials) { 61 return encryptCredentials.equals(createCredentials(credentials, salt)); 62 } 63 64 public static void main(String[] args) { 65 //盐 66 String salt = createSalt(); 67 System.out.println(salt); 68 System.out.println(salt.length()); 69 //凭证+盐加密后得到的密码 70 String credentials = createCredentials("123", salt); 71 System.out.println(credentials); 72 System.out.println(credentials.length()); 73 boolean b = checkCredentials("123", salt, credentials); 74 System.out.println(b); 75 } 76 }
Jsp页面
login.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <html> 3 <head> 4 <title>Title</title> 5 </head> 6 <body> 7 <h1>用户登陆</h1> 8 <div style="color: red">${message}</div> 9 <form action="${pageContext.request.contextPath}/login" method="post"> 10 帐号:<input type="text" name="username"><br> 11 密码:<input type="password" name="password"><br> 12 <input type="submit" value="确定"> 13 <input type="reset" value="重置"> 14 </form> 15 </body> 16 </html>
register.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <html> 3 <head> 4 <title>Title</title> 5 </head> 6 <body> 7 <h1>用户注册</h1> 8 <div style="color: red">${message}</div> 9 <form action="${pageContext.request.contextPath}/register" method="post"> 10 帐号:<input type="text" name="username"><br> 11 密码:<input type="password" name="password"><br> 12 <input type="submit" value="注册"> 13 <input type="button" οnclick="location.href='${pageContext.request.contextPath}/login.jsp'" value="返回"> 14 </form> 15 </body> 16 </html>
main.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <%@taglib prefix="r" uri="http://shiro.apache.org/tags" %> 3 <html> 4 <head> 5 <title>Title</title> 6 </head> 7 <body> 8 <h1>主界面<%=System.currentTimeMillis()%>,欢迎您:[${sessionScope.username}]</h1> 9 <ul> 10 系统功能列表 11 <li> 12 <a href="admin/addUser.jsp">用户新增</a> 13 </li> 14 <li> 15 <a href="admin/listUser.jsp">用户查询</a> 16 </li> 17 <li> 18 <a href="admin/resetPwd.jsp">重置用户密码</a> 19 </li> 20 <li> 21 <a href="admin/updateUser.jsp">用户修改</a> 22 </li> 23 <li> 24 <a href="user/updatePwd.jsp">个人密码修改</a> 25 </li> 26 <li> 27 <a href="user/teacher.jsp">老师简介</a> 28 </li> 29 <li> 30 <a href="${pageContext.request.contextPath}/logout">退出系统</a> 31 </li> 32 </ul> 33 <ul> 34 shiro标签 35 <li> 36 <r:hasPermission name="user:create"> 37 <a href="admin/addUser.jsp">用户新增</a> 38 </r:hasPermission> 39 </li> 40 <li> 41 <a href="admin/listUser.jsp">用户查询</a> 42 </li> 43 <li> 44 <a href="admin/resetPwd.jsp">重置用户密码</a> 45 </li> 46 <li> 47 <r:hasPermission name="user:update"> 48 <a href="admin/updateUser.jsp">用户修改</a> 49 </r:hasPermission> 50 </li> 51 <li> 52 <a href="user/updatePwd.jsp">个人密码修改</a> 53 </li> 54 <li> 55 <a href="${pageContext.request.contextPath}/logout">退出系统</a> 56 </li> 57 </ul> 58 </body> 59 </html>
运行结果