一、登录认证
即在应用中谁能证明他就是他本人。一般提供如他们的身份ID 一些标识信息来表明他就是他本人,如提供身份证,用户名/密码来证明。
在 shiro 中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能验证用户身份。
二、Shiro登录认证基本概念
1,principals
身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
2, credentials
证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见的principals和credentials组合就是用户名/密码了。接下来先进行一个基本的身份认证。
三、Shiro登录认证流程
1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;
2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
5、Authenticator 会把相应的token 传入Realm,从Realm 获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。
四、Shiro在SpringBoot中Demo
Config.java
1 package io.guangsoft.shiro.config;
2
3 import java.util.LinkedHashMap;
4 import java.util.Map;
5
6 import io.guangsoft.shiro.realm.Realm;
7 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
8 import org.apache.shiro.mgt.RememberMeManager;
9 import org.apache.shiro.mgt.SecurityManager;
10 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
11 import org.apache.shiro.web.mgt.CookieRememberMeManager;
12 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
13 import org.apache.shiro.web.servlet.SimpleCookie;
14 import org.springframework.context.annotation.Bean;
15 import org.springframework.context.annotation.Configuration;
16
17 @Configuration
18 public class Config {
19
20 @Bean
21 public Realm realmManager() {
22 // 加密相关
23 HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
24 // 散列算法
25 hashedCredentialsMatcher.setHashAlgorithmName("md5");
26 // 散列次数
27 hashedCredentialsMatcher.setHashIterations(2);
28 Realm realm = new Realm();
29 realm.setCredentialsMatcher(hashedCredentialsMatcher);
30 return realm;
31 }
32
33 @Bean
34 public RememberMeManager rememberMeManager() {
35 CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
36 //注入自定义cookie(主要是设置寿命, 默认的一年太长)
37 SimpleCookie simpleCookie = new SimpleCookie();
38 simpleCookie.setHttpOnly(true);
39 //设置RememberMe的cookie有效期为7天
40 simpleCookie.setMaxAge(604800);
41 rememberMeManager.setCookie(simpleCookie);
42 return rememberMeManager;
43 }
44
45 @Bean
46 public SecurityManager securityManager() {
47 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
48 securityManager.setRealm(realmManager());
49 securityManager.setRememberMeManager(rememberMeManager());
50 return securityManager;
51 }
52
53 @Bean
54 public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
55 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
56 shiroFilterFactoryBean.setSecurityManager(securityManager);
57 //当用户未登录则跳转到登录路径
58 shiroFilterFactoryBean.setLoginUrl("/login");
59 //登录成功后要跳转的链接,表单登录方式有效
60 shiroFilterFactoryBean.setSuccessUrl("/index");
61 //未授权界面,指定没有权限操作时跳转页面
62 shiroFilterFactoryBean.setUnauthorizedUrl("/warning");
63 //配置不会被过滤的链接顺序判断,过虑器链定义,从上向下顺序执行,一般将/**放在最下边
64 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
65 //对静态资源设置匿名访问,anon:所有url都可以匿名访问
66 filterChainDefinitionMap.put("/assets/**", "anon");
67 //放开登录接口,允许进行登录操作
68 filterChainDefinitionMap.put("/shiro/login", "anon");
69 //配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
70 filterChainDefinitionMap.put("/shiro/logout", "logout");
71 //authc:所有url都必须认证通过才可以访问
72 filterChainDefinitionMap.put("/**", "authc");
73 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
74 return shiroFilterFactoryBean;
75 }
76
77 }
Realm.java
1 package io.guangsoft.shiro.realm;
2
3 import org.apache.shiro.authc.AuthenticationException;
4 import org.apache.shiro.authc.AuthenticationInfo;
5 import org.apache.shiro.authc.AuthenticationToken;
6 import org.apache.shiro.authc.SimpleAuthenticationInfo;
7 import org.apache.shiro.authz.AuthorizationInfo;
8 import org.apache.shiro.authz.SimpleAuthorizationInfo;
9 import org.apache.shiro.realm.AuthorizingRealm;
10 import org.apache.shiro.subject.PrincipalCollection;
11 import org.apache.shiro.util.ByteSource;
12
13 import java.util.ArrayList;
14 import java.util.List;
15
16 public class Realm extends AuthorizingRealm {
17
18 //认证,模拟从数据库查询出密码
19 @Override
20 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
21 String userName = (String) token.getPrincipal();
22 String password = "4d521acb9b8b3b4fa082ab16b3bd363a";
23 String salt = "guanghe";
24 return new SimpleAuthenticationInfo(userName, password, ByteSource.Util.bytes(salt), this.getName());
25 }
26 }
ShiroController.java
1 package io.guangsoft.shiro.controller;
2
3 import com.alibaba.fastjson.JSONObject;
4 import org.apache.shiro.SecurityUtils;
5 import org.apache.shiro.authc.*;
6 import org.apache.shiro.subject.Subject;
7 import org.springframework.web.bind.annotation.RequestMapping;
8 import org.springframework.web.bind.annotation.RestController;
9
10 @RestController
11 @RequestMapping("shiro")
12 public class ShiroController {
13
14 @RequestMapping(value = "login")
15 public String login(String username, String password, Boolean rememberMe) {
16 Subject subject = SecurityUtils.getSubject();
17 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
18 usernamePasswordToken.setRememberMe(rememberMe);
19 String msg = null;
20 try {
21 subject.login(usernamePasswordToken);
22 msg = "身份认证成功!";
23 } catch (UnknownAccountException e) {
24 e.printStackTrace();
25 msg = "账号不存在!";
26 } catch (LockedAccountException e) {
27 e.printStackTrace();
28 msg = "账号被锁定!";
29 } catch (DisabledAccountException e) {
30 e.printStackTrace();
31 msg = "账号被禁用!";
32 } catch (IncorrectCredentialsException e) {
33 e.printStackTrace();
34 msg = "凭证/密码错误!";
35 } catch (ExpiredCredentialsException e) {
36 e.printStackTrace();
37 msg = "凭证/密码过期!";
38 } catch (ExcessiveAttemptsException e) {
39 e.printStackTrace();
40 msg = "登录失败次数过多!";
41 }
42 JSONObject result = new JSONObject();
43 if(subject.isAuthenticated()) {
44 result.put("code", 0);
45 } else {
46 result.put("code", -1);
47 }
48 result.put("msg", msg);
49 return result.toString();
50 }
51 }