Shiro认证、盐加密
一、
1.1 导入pom.xml
1.2.5
org.apache.shiro
shiro-core
${shiro.version}
org.apache.shiro
shiro-web
${shiro.version}
org.apache.shiro
shiro-spring
${shiro.version}
1.2 配置web.xml
1
2
3 shiroFilter
4 org.springframework.web.filter.DelegatingFilterProxy
5
6
7 targetFilterLifecycle
8 true
9
10
11
12 shiroFilter
13 /*
14
1.3 从数据库表逆向生成对应的model、mapper类
二、实践、操作
2.1 ShiroUserMapper
1 package com.jy.mapper;
2
3 import com.jy.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 }
2.2 ShiroUserMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14 userid, username, PASSWORD, salt, createdate
15
16
22
23 delete from t_shiro_user
24 where userid = #{userid,jdbcType=INTEGER}
25
26
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
32
33 insert into t_shiro_user
34
35
36 userid,
37
38
39 username,
40
41
42 PASSWORD,
43
44
45 salt,
46
47
48 createdate,
49
50
51
52
53 #{userid,jdbcType=INTEGER},
54
55
56 #{username,jdbcType=VARCHAR},
57
58
59 #{password,jdbcType=VARCHAR},
60
61
62 #{salt,jdbcType=VARCHAR},
63
64
65 #{createdate,jdbcType=TIMESTAMP},
66
67
68
69
70 update t_shiro_user
71
72
73 username = #{username,jdbcType=VARCHAR},
74
75
76 PASSWORD = #{password,jdbcType=VARCHAR},
77
78
79 salt = #{salt,jdbcType=VARCHAR},
80
81
82 createdate = #{createdate,jdbcType=TIMESTAMP},
83
84
85 where userid = #{userid,jdbcType=INTEGER}
86
87
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
95
96
102
103
104
2.3 Service
1 package com.jy.service;
2
3 import com.jy.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 }
2.4 Service.impl
package com.jy.service.impl;
import com.jy.mapper.ShiroUserMapper;
import com.jy.model.ShiroUser;
import com.jy.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);
}
}
2.5 MyRealm
1 package com.jy.shiro;
2
3 import com.jy.mapper.ShiroUserMapper;
4 import com.jy.model.ShiroUser;
5 import com.jy.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 }
2.6 applicationContext-shiro.xml
1
2
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
47
48
49
50 /user/login=anon
51 /user/updatePwd.jsp=authc
52 /admin/*.jsp=roles[admin]
53 /user/teacher.jsp=perms["user:update"]
54
64
65
66
67
68
69
70
注册、登录
2.7 ShiroUserController
1 package com.jy.controller;
2
3
4 import com.jy.model.ShiroUser;
5 import com.jy.service.ShiroUserService;
6 import com.jy.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 }
三、盐加密的工具类
3.1 PasswordHelper
1 package com.jy.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 }
页面展示
3.2 login.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2
3
4 Title
5
6
7 用户登陆
8 ${message}
9
15
16
3.3 register.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2
3
4 Title
5
6
7 用户注册
8 ${message}
9
15
16
3.4 main.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="r" uri="http://shiro.apache.org/tags" %>
3
4
5 Title
6
7
8 主界面<%=System.currentTimeMillis()%>,欢迎您:[${sessionScope.username}]
9
33
58
59