ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

shiro整合SSM(spring_springmvc_mybatis)

2022-05-21 21:00:29  阅读:186  来源: 互联网

标签:username springmvc spring SSM admin user org import shiro


SSM(示例代码码云地址:https://gitee.com/joy521125/ssm-senior-base.git)

SSM的maven版本(示例代码码云地址:https://gitee.com/joy521125/ssm-senior.git);

查询ssm整合shiro 请查看shiro分支;

一、加入的jar包和目录结构:

  • ehcache-core-2.5.7.jar
  • encoder-1.2.3.jar
  • shiro-all-1.9.0.jar
  • slf4j-api-1.7.32.jar
  • slf4j-log4j12-1.7.12.jar

  

 

二、整合ssm 的基本配置

shiro.xml配置;

参考地址参考地址 /shiro-root-1.3.2/samples/web/src/main/webapp/WEB-INF/applicationContext.xml;

1.配置securityManager

2.配置缓存管理器 :需要加入ehcache的jar包及配置文件(ehcache.xml 可参考六、mybatis 第三方缓存之ehcache

3.配置realm :配置了直接实现Realm的接口

4.配置了lifecycleBeanPostProcessor:可以自定义的来调用配置在spring IOC 容器中 shiro bean 的生命周期方法

5.启用IOC容器中使用shiro的注解,但必须在配置了lifecycleBeanPostProcessor之后才能使用

6.配置shiroFilter;

  • id 必须和web.xml文件中配置的DelegatingFilterProxy的<filter-name></filter-name>一致;若不一致,则会抛出:NOSuchBeanDefinitionException.因为Shiro回来IOC容器中查找<filter-name></filter-name>名字对应的filter bean
  • 过滤器格式:url=拦截器[参数]
  • URL匹配顺序:URL 权限采取第一次匹配优先的方式,即从头开始 使用第一个匹配的 url 模式对应的拦截器链。• 如:

– /bb/**=filter1

– /bb/aa=filter2

– /**=filter3

– 如果请求的url是“/bb/aa”,因为按照声明顺序进行匹 配,那么将使用 filter1 进行拦截。

  • url 模式使用 Ant 风格模式 • Ant 路径通配符支持 ?、 * 、 **,注意通配符匹配不 包括目录分隔符“/”:

– ?:匹配一个字符,如 /admin? 将匹配 /admin1,但不 匹配 /admin 或 /admin/;

– *:匹配零个或多个字符串,如 /admin* 将匹配 /admin、 /admin123,但不匹配 /admin/1;

– **:匹配路径中的零个或多个路径,如 /admin/** 将匹 配 /admin/a 或 /admin/a/b

  • 登入地址/认证失败地址:loginUrl
  • 认证和授权成功地址:successUrl
  • 认证成功授权失败地址:unauthorizedUrl
  •  过滤器:
过滤器 说明
authc 需要认证登录才能访问
user 用户拦截器,表示必须存在用户。
anon 匿名拦截器,不需要登录即可访问的资源,匿名用户或游客,一般用于过滤静态资源。
roles

角色授权拦截器,验证用户是或否拥有角色。

参数可写多个,表示某些角色才能通过,多个参数时写 roles[“admin,user”],当有多个参数时必须每个
参数都通过才算通过

authcBasic httpBasic 身份验证拦截器。
logout 退出拦截器,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 设置的 url
port 端口拦截器, 可通过的端口。
ssl ssl拦截器,只有请求协议是https才能通过

 拦截器可以静态配置(在shiro.xml中写死过滤器),也可以动态配置(可从数据库读取);选一种即可;

loginUrl&successUrl&unauthorizedUrl 地址对应的Controller(HomeController.java)

 1 package ssm.controller;
 2 
 3 import org.apache.shiro.SecurityUtils;
 4 import org.apache.shiro.authc.AuthenticationException;
 5 import org.apache.shiro.authc.IncorrectCredentialsException;
 6 import org.apache.shiro.authc.LockedAccountException;
 7 import org.apache.shiro.authc.UnknownAccountException;
 8 import org.apache.shiro.authc.UsernamePasswordToken;
 9 import org.apache.shiro.subject.Subject;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12 import org.springframework.stereotype.Controller;
13 import org.springframework.web.bind.annotation.RequestMapping;
14 import org.springframework.web.bind.annotation.RequestMethod;
15 import org.springframework.web.bind.annotation.RequestParam;
16 import org.springframework.web.servlet.ModelAndView;
17 
18 @Controller
19 public class HomeController {
20     private static final transient Logger log = LoggerFactory.getLogger(HomeController.class);
21 
22     @RequestMapping(value = "/login", method = RequestMethod.GET)
23     public ModelAndView login() {
24         ModelAndView mv = new ModelAndView("login");
25         return mv;
26     }
27 
28     @RequestMapping(value = "/role/admin", method = RequestMethod.GET)
29     public ModelAndView admin() {
30         ModelAndView mv = new ModelAndView("admin");
31         return mv;
32     }
33 
34     @RequestMapping(value = "/role/user", method = RequestMethod.GET)
35     public ModelAndView user() {
36         ModelAndView mv = new ModelAndView("user");
37         return mv;
38     }
39 
40     @RequestMapping(value = "/error/403", method = RequestMethod.GET)
41     public ModelAndView error() {
42         ModelAndView mv = new ModelAndView("unauthorized");
43         return mv;
44     }
45 
46     @RequestMapping(value = "/login", method = RequestMethod.POST)
47     public String login(@RequestParam(value = "username") String useranme,
48             @RequestParam(value = "password") String password) {
49         Subject currentUser = SecurityUtils.getSubject();
50         if (!currentUser.isAuthenticated()) {
51             // 用户名和密码封装为UsernamePasswordToken对象
52             UsernamePasswordToken token = new UsernamePasswordToken(useranme, password);
53             // 设置记住我
54             token.setRememberMe(true);
55             try {
56                 System.out.println(token.hashCode());
57                 // 执行登入
58                 currentUser.login(token);
59                 // 若没有指定的账户,则shiro将抛出UnknownAccountException
60             } catch (UnknownAccountException uae) {
61                 log.info("There is no user with username of " + token.getPrincipal());
62                 // 若账户存在,但密码不匹配,则shiro将抛出IncorrectCredentialsException
63             } catch (IncorrectCredentialsException ice) {
64                 log.info("Password for account " + token.getPrincipal() + " was incorrect!");
65                 // 若用户被锁定,抛出LockedAccountException
66             } catch (LockedAccountException lae) {
67                 log.info("The account for username " + token.getPrincipal() + " is locked.  "
68                         + "Please contact your administrator to unlock it.");
69             }
70             // 所有上面认证异常的父类
71             // ... catch more exceptions here (maybe custom ones specific to your
72             // application?
73             catch (AuthenticationException ae) {
74                 log.info("登入失败:" + ae.getMessage());
75             }
76         }
77         return "redirect:emp/list";
78     }
79 
80 }
View Code

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="
  5        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
  6 
  7 
  8     <!-- ========================================================= Shiro Core 
  9         Components - Not Spring Specific ========================================================= -->
 10     <!-- Shiro's main business-tier object for web-enabled applications (use 
 11         DefaultSecurityManager instead when there is no web environment) -->
 12     <!-- 1.配置securityManager -->
 13     <bean id="securityManager"
 14         class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
 15         <property name="cacheManager" ref="cacheManager" />
 16         <!-- 配置单个realm -->
 17         <property name="realm" ref="jdbcRealm" /> 
 18     </bean>
 19     <!-- Let's use some enterprise caching support for better performance. You 
 20         can replace this with any enterprise caching framework implementation that 
 21         you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc -->
 22     <!-- 2.配置缓存管理器 2.1 需要加入ehcache的jar包及配置文件 2.2 -->
 23     <bean id="cacheManager"
 24         class="org.apache.shiro.cache.ehcache.EhCacheManager">
 25         <!-- <property name="cacheManager" ref="ehCacheManager" /> -->
 26         <property name="cacheManagerConfigFile"
 27             value="classpath:ehcache.xml" />
 28     </bean>
 29 
 30     <!-- Used by the SecurityManager to access security data (users, roles, 
 31         etc). Many other realm implementations can be used too (PropertiesRealm, 
 32         LdapRealm, etc. -->
 33     <!-- 3.配置realm 3.1配置了直接实现Realm的接口 -->
 34     <bean id="jdbcRealm" class="ssm.shiro.ShiroRealm">
 35         <property name="credentialsMatcher">
 36             <bean
 37                 class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
 38                 <!-- 指定加密算法 -->
 39                 <property name="hashAlgorithmName" value="MD5"></property>
 40                 <!-- 可以指定加密的次数 -->
 41                 <property name="hashIterations" value="1024"></property>
 42                 <property name="storedCredentialsHexEncoded" value="true"/>
 43             </bean>
 44         </property>
 45     </bean>
 46 
 47     <!-- ========================================================= Shiro Spring-specific 
 48         integration ========================================================= -->
 49     <!-- Post processor that automatically invokes init() and destroy() methods 
 50         for Spring-configured Shiro objects so you don't have to 1) specify an init-method 
 51         and destroy-method attributes for every bean definition and 2) even know 
 52         which Shiro objects require these methods to be called. -->
 53     <!-- 4.配置了lifecycleBeanPostProcessor,可以自定义的来调用配置在spring IOC 容器中 shiro bean 
 54         的生命周期方法 -->
 55     <bean id="lifecycleBeanPostProcessor"
 56         class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
 57 
 58     <!-- Enable Shiro Annotations for Spring-configured beans. Only run after 
 59         the lifecycleBeanProcessor has run: -->
 60     <!-- 5.启用IOC容器中使用shiro的注解,但必须在配置了lifecycleBeanPostProcessor之后才能使用 -->
 61     <bean
 62         class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
 63         depends-on="lifecycleBeanPostProcessor" />
 64     <bean
 65         class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
 66         <property name="securityManager" ref="securityManager" />
 67     </bean>
 68 
 69 
 70 
 71     <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly 
 72         in web.xml - web.xml uses the DelegatingFilterProxy to access this bean. 
 73         This allows us to wire things with more control as well utilize nice Spring 
 74         things such as PropertiesPlaceholderConfigurer and abstract beans or anything 
 75         else we might need: -->
 76     <!-- 6.配置shiroFilter 6.1 id 必须和web.xml文件中配置的DelegatingFilterProxy的<filter-name></filter-name>一致 
 77         若不一致,则会抛出:NOSuchBeanDefinitionException.因为Shiro回来IOC容器中查找<filter-name></filter-name>名字对应的filter 
 78         bean 6.2 DelegatingFilterProxy实际上是Filter的一个代理对象,默认情况下,Spring 会到IOC容器中查找和<filter-name>对应的filter 
 79         bean 也可以通过 targetBeanName的初始化参数来配置filter bean 的ID 6.3url匹配模式: ?:可以匹配一个字符:/admin? 
 80         /admin1 但是不匹配/admin或 /admin/ *:可以匹配零个或者多个字符串,比如 /admin*将匹配/admin、/admin123,但不匹配/admin/1 
 81         **:匹配路径中的零个或者多个路径,如:/admin**可以匹配 /admin/a 或者 /admin?/a/bb 6.4url • URL权限采取第一次匹配优先的方式,即从头开始 
 82         使用第一个匹配的 url 模式对应的拦截器链。 • 如: – /bb/**=filter1 – /bb/aa=filter2 – /**=filter3 
 83         – 如果请求的url是“/bb/aa”,因为按照声明顺序进行匹 配,那么将使用 filter1 进行拦截 -->
 84     <bean id="shiroFilter"
 85         class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
 86         <property name="securityManager" ref="securityManager" />
 87         <property name="loginUrl" value="/login" />
 88         <property name="successUrl" value="/emp/list" />
 89         <property name="unauthorizedUrl" value="/error/403" />
 90         <!-- 配置哪些页面需要受保护 以及访问这些页面需要的权限
 91          1.anon:可以被匿名访问 
 92          2.authc:必须认证(登入)才能访问 
 93          3).logout:登出
 94          4.roles角色过滤器 -->
 95          <property name="filterChainDefinitions">
 96             <value>
 97                 # allow WebStart to pull the jars for the swing app: 
 98                 /*.jar = anon
 99                 /login = anon
100                 /logout = logout
101                  /role/user=roles[user]
102                 /role/admin=roles[admin]
103                 
104                 # everything else requires authentication:
105                 /** = authc
106             </value>
107         </property> 
108     </bean>
109 </beans>
View Code

拦截器的动态配置:

 1 <bean id="shiroFilter"
 2         class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
 3         <property name="securityManager" ref="securityManager" />
 4         <property name="loginUrl" value="/login" />
 5         <property name="successUrl" value="/emp/list" />
 6         <property name="unauthorizedUrl" value="/error/403" />
 7         <!-- 配置哪些页面需要受保护 以及访问这些页面需要的权限
 8          1.anon:可以被匿名访问 
 9          2.authc:必须认证(登入)才能访问 
10          3).logout:登出
11          4.roles角色过滤器 -->
12         <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
13     </bean>
14     
15     <!-- 配置一个bean ,该bean实际上是一个map,通过实例工厂的方式 -->
16     <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder" factory-method="bulidFilterChainDefinitionMap"></bean>
17     <bean id="filterChainDefinitionMapBuilder" class="ssm.shiro.factory.FilterChainDefinitionMapBuilder"></bean>

FilterChainDefinitionMapBuilder:

package ssm.shiro.factory;

import java.util.LinkedHashMap;

public class FilterChainDefinitionMapBuilder {

    public LinkedHashMap<String, String> bulidFilterChainDefinitionMap() {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        // /login = anon
        // /logout = logout
        // /role/user=roles[user]
        // /role/admin=roles[admin]
        // # everything else requires authentication:
        // /** = authc
        map.put("/login", "anon");
        map.put("/logout", "logout");
        map.put("/js/**", "anon");
        map.put("/role/user", "roles[user]");
        map.put("/role/admin", "roles[admin]");
        map.put("/emp/list", "user");
        map.put("/**", "authc");
        return map;
    }
}
View Code

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="
  5        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
  6 
  7 
  8     <!-- ========================================================= Shiro Core 
  9         Components - Not Spring Specific ========================================================= -->
 10     <!-- Shiro's main business-tier object for web-enabled applications (use 
 11         DefaultSecurityManager instead when there is no web environment) -->
 12     <!-- 1.配置securityManager -->
 13     <bean id="securityManager"
 14         class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
 15         <property name="cacheManager" ref="cacheManager" />
 16         <!-- 配置单个realm -->
 17         <property name="realm" ref="jdbcRealm" /> 
 18     </bean>
 19     <!-- Let's use some enterprise caching support for better performance. You 
 20         can replace this with any enterprise caching framework implementation that 
 21         you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc -->
 22     <!-- 2.配置缓存管理器 2.1 需要加入ehcache的jar包及配置文件 2.2 -->
 23     <bean id="cacheManager"
 24         class="org.apache.shiro.cache.ehcache.EhCacheManager">
 25         <!-- <property name="cacheManager" ref="ehCacheManager" /> -->
 26         <property name="cacheManagerConfigFile"
 27             value="classpath:ehcache.xml" />
 28     </bean>
 29 
 30     <!-- Used by the SecurityManager to access security data (users, roles, 
 31         etc). Many other realm implementations can be used too (PropertiesRealm, 
 32         LdapRealm, etc. -->
 33     <!-- 3.配置realm 3.1配置了直接实现Realm的接口 -->
 34     <bean id="jdbcRealm" class="ssm.shiro.ShiroRealm">
 35         <property name="credentialsMatcher">
 36             <bean
 37                 class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
 38                 <!-- 指定加密算法 -->
 39                 <property name="hashAlgorithmName" value="MD5"></property>
 40                 <!-- 可以指定加密的次数 -->
 41                 <property name="hashIterations" value="1024"></property>
 42                 <property name="storedCredentialsHexEncoded" value="true"/>
 43             </bean>
 44         </property>
 45     </bean>
 46 
 47     <!-- ========================================================= Shiro Spring-specific 
 48         integration ========================================================= -->
 49     <!-- Post processor that automatically invokes init() and destroy() methods 
 50         for Spring-configured Shiro objects so you don't have to 1) specify an init-method 
 51         and destroy-method attributes for every bean definition and 2) even know 
 52         which Shiro objects require these methods to be called. -->
 53     <!-- 4.配置了lifecycleBeanPostProcessor,可以自定义的来调用配置在spring IOC 容器中 shiro bean 
 54         的生命周期方法 -->
 55     <bean id="lifecycleBeanPostProcessor"
 56         class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
 57 
 58     <!-- Enable Shiro Annotations for Spring-configured beans. Only run after 
 59         the lifecycleBeanProcessor has run: -->
 60     <!-- 5.启用IOC容器中使用shiro的注解,但必须在配置了lifecycleBeanPostProcessor之后才能使用 -->
 61     <bean
 62         class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
 63         depends-on="lifecycleBeanPostProcessor" />
 64     <bean
 65         class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
 66         <property name="securityManager" ref="securityManager" />
 67     </bean>
 68 
 69 
 70 
 71     <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly 
 72         in web.xml - web.xml uses the DelegatingFilterProxy to access this bean. 
 73         This allows us to wire things with more control as well utilize nice Spring 
 74         things such as PropertiesPlaceholderConfigurer and abstract beans or anything 
 75         else we might need: -->
 76     <!-- 6.配置shiroFilter 6.1 id 必须和web.xml文件中配置的DelegatingFilterProxy的<filter-name></filter-name>一致 
 77         若不一致,则会抛出:NOSuchBeanDefinitionException.因为Shiro回来IOC容器中查找<filter-name></filter-name>名字对应的filter 
 78         bean 6.2 DelegatingFilterProxy实际上是Filter的一个代理对象,默认情况下,Spring 会到IOC容器中查找和<filter-name>对应的filter 
 79         bean 也可以通过 targetBeanName的初始化参数来配置filter bean 的ID 6.3url匹配模式: ?:可以匹配一个字符:/admin? 
 80         /admin1 但是不匹配/admin或 /admin/ *:可以匹配零个或者多个字符串,比如 /admin*将匹配/admin、/admin123,但不匹配/admin/1 
 81         **:匹配路径中的零个或者多个路径,如:/admin**可以匹配 /admin/a 或者 /admin?/a/bb 6.4url • URL权限采取第一次匹配优先的方式,即从头开始 
 82         使用第一个匹配的 url 模式对应的拦截器链。 • 如: – /bb/**=filter1 – /bb/aa=filter2 – /**=filter3 
 83         – 如果请求的url是“/bb/aa”,因为按照声明顺序进行匹 配,那么将使用 filter1 进行拦截 -->
 84     <bean id="shiroFilter"
 85         class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
 86         <property name="securityManager" ref="securityManager" />
 87         <property name="loginUrl" value="/login" />
 88         <property name="successUrl" value="/emp/list" />
 89         <property name="unauthorizedUrl" value="/error/403" />
 90         <!-- 配置哪些页面需要受保护 以及访问这些页面需要的权限
 91          1.anon:可以被匿名访问 
 92          2.authc:必须认证(登入)才能访问 
 93          3).logout:登出
 94          4.roles角色过滤器 -->
 95         <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
 96     </bean>
 97     
 98     <!-- 配置一个bean ,该bean实际上是一个map,通过实例工厂的方式 -->
 99     <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder" factory-method="bulidFilterChainDefinitionMap"></bean>
100     <bean id="filterChainDefinitionMapBuilder" class="ssm.shiro.factory.FilterChainDefinitionMapBuilder"></bean>
101 </beans>
View Code

 

web.xml配置:

参考地址 /shiro-root-1.3.2/samples/web/src/main/webapp/WEB-INF/web.xml;

需要修改两个地方:1.添加shiro filter 2.读取配置文件需要添加shiro.xml 和springmvc.xml(由于Realm中需要使用自动装配@Autowired 所以需要在这里加入,否则就报无法找到 自动装配的bean):

a.需要添加shiroFilter

 1  <filter>
 2         <filter-name>shiroFilter</filter-name>
 3         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
 4         <init-param>
 5             <param-name>targetFilterLifecycle</param-name>
 6             <param-value>true</param-value>
 7         </init-param>
 8     </filter>
 9 
10     <filter-mapping>
11         <filter-name>shiroFilter</filter-name>
12         <url-pattern>/*</url-pattern>
13     </filter-mapping>

b.读取配置文件额外添加shiro.xml 和 springmvc配置文件

1 <context-param>
2         <param-name>contextConfigLocation</param-name>
3         <param-value>classpath:springmvc.xml,classpath:spring.xml,classpath:shiro.xml</param-value>
4     </context-param>

web.xml完整文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 4     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
 5     version="3.1">
 6     <context-param>
 7         <param-name>contextConfigLocation</param-name>
 8         <param-value>classpath:springmvc.xml,classpath:spring.xml,classpath:shiro.xml</param-value>
 9     </context-param>
10     <filter>
11         <filter-name>CharacterEncodingFilter</filter-name>
12         <filter-class>org.springframework.web.filter.CharacterEncodingFilter
13         </filter-class>
14         <init-param>
15             <param-name>encoding</param-name>
16             <param-value>utf-8</param-value>
17         </init-param>
18     </filter>
19     <filter-mapping>
20         <filter-name>CharacterEncodingFilter</filter-name>
21         <url-pattern>/*</url-pattern>
22     </filter-mapping>
23     <listener>
24         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
25     </listener>
26     <!-- 配置DispatcherServlet(快捷键 alt +/) -->
27     <servlet>
28         <servlet-name>springDispatcherServlet</servlet-name>
29         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
30         <!-- 配置DispatcherServletd 一个初始化参数:配置springmvc配置文件的位置和名称 -->
31         <!-- 实际上也可以不通过 contextConfigLocation 来配置Springmvc的配置文件,而是用默认的 即默认的配置文件为 
32             /WEB-INF/<servlet-name>-servlet.xml 本项目默认位置配置文件即为: /WEB-INF/springDispatcherServlet-servlet.xml -->
33         <init-param>
34             <param-name>contextConfigLocation</param-name>
35             <param-value>classpath:springmvc.xml</param-value>
36         </init-param>
37         <!-- 表示springDispatcherServlet在加载的时候被创建 -->
38         <load-on-startup>1</load-on-startup>
39     </servlet>
40 
41     <!-- Map all requests to the DispatcherServlet for handling -->
42     <servlet-mapping>
43         <servlet-name>springDispatcherServlet</servlet-name>
44         <url-pattern>/</url-pattern>
45     </servlet-mapping>
46     
47     <!-- 1.配置shiro 的shiroFilter(/shiro-root-1.3.2/samples/web/src/main/webapp/WEB-INF/web.xml) -->
48      <filter>
49         <filter-name>shiroFilter</filter-name>
50         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
51         <init-param>
52             <param-name>targetFilterLifecycle</param-name>
53             <param-value>true</param-value>
54         </init-param>
55     </filter>
56 
57     <filter-mapping>
58         <filter-name>shiroFilter</filter-name>
59         <url-pattern>/*</url-pattern>
60     </filter-mapping>
61 </web-app>
View Code

 

 三、身份认证:

认证步骤:

1.获取当前的Subject。调用 SecurityUtils.getSubject();
2.测试当前用户是否已经被认证。即是否已经登入。调用Subject 的 isAuthenticated();
3.若没有被认证,则把用户名和密码封装为UsernamePasswordToken对象,表单页面用来获取用户名密码;

1). 创建一个表单页面;
2).把请求提交到springMVC的handler
3).获取用户名和密码

4.执行登入:调用Subject 的 login(AuthenticationToken)方法;
5.自定义Realm的方法,从数据库中获取对应的记录,返回给Shiro。

1).实际上需要继承 org.apache.shiro.realm.AuthenticatingRealm类
2).实现doGetAuthenticationInfo(AuthenticationToken)方法;

6.由shiro完成对密码的比对:密码的比对:通过AuthenticatingRealm的credentialsMatcher属性来进行的密码比对

获取当前的Subject&当前用户是否已经被认证:

执行登入currentUser.login(token)时,实际上调用的是:Realm中的doGetAuthenticationInfo方法;

 1 @RequestMapping(value = "/login", method = RequestMethod.POST)
 2     public String login(@RequestParam(value = "username") String useranme,
 3             @RequestParam(value = "password") String password) {
 4         Subject currentUser = SecurityUtils.getSubject();
 5         if (!currentUser.isAuthenticated()) {
 6             // 用户名和密码封装为UsernamePasswordToken对象
 7             UsernamePasswordToken token = new UsernamePasswordToken(useranme, password);
 8             // 设置记住我
 9             token.setRememberMe(true);
10             try {
11                 System.out.println(token.hashCode());
12                 // 执行登入
13                 currentUser.login(token);
14                 // 若没有指定的账户,则shiro将抛出UnknownAccountException
15             } catch (UnknownAccountException uae) {
16                 log.info("There is no user with username of " + token.getPrincipal());
17                 // 若账户存在,但密码不匹配,则shiro将抛出IncorrectCredentialsException
18             } catch (IncorrectCredentialsException ice) {
19                 log.info("Password for account " + token.getPrincipal() + " was incorrect!");
20                 // 若用户被锁定,抛出LockedAccountException
21             } catch (LockedAccountException lae) {
22                 log.info("The account for username " + token.getPrincipal() + " is locked.  "
23                         + "Please contact your administrator to unlock it.");
24             }
25             // 所有上面认证异常的父类
26             // ... catch more exceptions here (maybe custom ones specific to your
27             // application?
28             catch (AuthenticationException ae) {
29                 log.info("登入失败:" + ae.getMessage());
30             }
31         }
32         return "redirect:emp/list";
33     }
View Code

自定义Realm实现(AuthenticatingRealm方式):

实现方式:AuthenticatingRealm 和AuthorizingRealm方式;一般使用AuthorizingRealm方式,因为除了带认证,还有授权;区别就是:AuthenticatingRealm 只有认证,而 AuthorizingRealm 有认证、有授权;

  • principals:身份,即主体的标识属性,可以是任何属性,如用户名、 邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是用户名/邮箱/手机号。
  • credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证 书等。 
  • 最常见的 principals 和 credentials 组合就是用户名/密码了
 1 public class ShiroRealm extends AuthenticatingRealm {
 2 
 3     @Autowired
 4     private UserService userService;
 5 
 6     @Override
 7     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
 8         System.out.println("doGetAuthenticationInfo:" + token.hashCode());
 9         // 1.把AuthenticationToken 强转为usernamePasswordToken
10         UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
11         // 2.usernamePasswordTokenh获取username
12         String username = usernamePasswordToken.getUsername();
13         // 3.调用数据库方法,从数据库中查询username对应的用户记录
14         System.out.println("从数据库获取username:" + username + ",所对应的记录");
15         User user = userService.getByUser(username);
16         // 4.若用户不存在,则可以抛出UnknownAccountException异常;
17         if (user != null) {
18             if ("unknown".equals(username)) {
19                 throw new UnknownAccountException();
20             }
21             // 5.根据用户信息的情况,决定是否需要抛出其他的AuthenticcationException异常
22             if ("monster".equals(username)) {
23                 throw new LockedAccountException();
24             }
25             // 6.根据用户的情况,来构建AuthenticationInfo
26             // 以下信息时从数据库中获取的
27             // 1).principal:实体的认证信息,可以是username,也可以是数据表对应的用户的实体类对象
28             Object principal = user;
29             // 2).credentials:密码(后台查询得到的密码)
30             Object credentials = user.getPassword();
31             // 3).realmName:当前realm对象的name,调用父类getName()方法即可;
32             String realmName = getName();
33             // 4).盐值
34             ByteSource salt = ByteSource.Util.bytes(username);
35             // SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
36             SimpleAuthenticationInfo info2 = new SimpleAuthenticationInfo(principal, credentials, salt, realmName);
37             return info2;
38         } else {
39             throw new UserException();
40         }
41 
42     }
43 
44 }
View Code

 运行代码,输入正确的用户名密码,就可以登入到配置的首页了即:emp/list;如果直接用AuthorizingRealm 方式:即如下方式;

 1 package ssm.shiro;
 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.LockedAccountException;
 7 import org.apache.shiro.authc.SimpleAuthenticationInfo;
 8 import org.apache.shiro.authc.UnknownAccountException;
 9 import org.apache.shiro.authc.UsernamePasswordToken;
10 import org.apache.shiro.authz.AuthorizationInfo;
11 import org.apache.shiro.crypto.hash.SimpleHash;
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.beans.factory.annotation.Autowired;
16 
17 import ssm.entity.User;
18 import ssm.service.UserService;
19 import ssm.utils.UserException;
20 
21 public class ShiroRealm extends AuthorizingRealm {
22 
23     @Autowired
24     private UserService userService;
25 
26     @Override
27     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
28         System.out.println("doGetAuthenticationInfo:" + token.hashCode());
29         // 1.把AuthenticationToken 强转为usernamePasswordToken
30         UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
31         // 2.usernamePasswordTokenh获取username
32         String username = usernamePasswordToken.getUsername();
33         // 3.调用数据库方法,从数据库中查询username对应的用户记录
34         System.out.println("从数据库获取username:" + username + ",所对应的记录");
35         User user = userService.getByUser(username);
36         // 4.若用户不存在,则可以抛出UnknownAccountException异常;
37         if (user != null) {
38             if ("unknown".equals(username)) {
39                 throw new UnknownAccountException();
40             }
41             // 5.根据用户信息的情况,决定是否需要抛出其他的AuthenticcationException异常
42             if ("monster".equals(username)) {
43                 throw new LockedAccountException();
44             }
45             // 6.根据用户的情况,来构建AuthenticationInfo
46             // 以下信息时从数据库中获取的
47             // 1).principal:实体的认证信息,可以是username,也可以是数据表对应的用户的实体类对象
48             Object principal = user;
49             // 2)。credentials:密码(后台查询得到的密码)
50             Object credentials = user.getPassword();
51             // 3)。realmName:当前realm对象的name,调用父类getName()方法即可;
52             String realmName = getName();
53             // 4).盐值
54             ByteSource salt = ByteSource.Util.bytes(username);
55             // SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
56             SimpleAuthenticationInfo info2 = new SimpleAuthenticationInfo(principal, credentials, salt, realmName);
57             return info2;
58         } else {
59             throw new UserException();
60         }
61 
62     }
63 
64     // 授权会被shiro回调的方法
65     @Override
66     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
67         return null;
68     }
69 }
View Code

加密

盐值加密

盐值加密的作用:防止 如果用户名不同,但是密码相同时,MD5加密后 ,密码相同;

步骤:

1)。doGetAuthenticationInfo的方法返回值创建SimpleAuthenticationInfo对象时,通过 SimpleAuthenticationInfo(principal, credentials, salt, realmName)
构造器创建;
2)。使用ByteSource.Util.bytes 来计算盐值
3)。盐值需要唯一:一般使用userid;
4).使用impleHash(hashAlgorithmName, credentials, salt, hashIterations)来计算盐值加密后的密码值;

前端页面密码加密配置(shiro.xml中的配置)

 1 <bean id="jdbcRealm" class="ssm.shiro.ShiroRealm">
 2         <property name="credentialsMatcher">
 3             <bean
 4                 class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
 5                 <!-- 指定加密算法 -->
 6                 <property name="hashAlgorithmName" value="MD5"></property>
 7                 <!-- 可以指定加密的次数 -->
 8                 <property name="hashIterations" value="1024"></property>
 9                 <property name="storedCredentialsHexEncoded" value="true"/>
10             </bean>
11         </property>
12     </bean>

md5 盐值加密:

1 public static void main(String[] args) {
2         String hashAlgorithmName = "MD5";
3         Object credentials = "123456";
4         Object salt = null;
5         int hashIterations = 1024;
6         SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
7         System.out.println(simpleHash);
8     }

四、授权

Shiro 支持三种方式的授权:

  • 编程式:通过写if/else 授权代码块完成
  • 注解式:通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相 应的异常
  • JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成 

Shiro 提供了 JSTL 标签

用于在 JSP 页面进行权限控制,如 根据登录用户显示相应的页面按钮。 • guest 标签:用户没有身份验证时显示相应信息,即游客 访问信息:

实现方式:

授权java代码:

 1     // 授权会被shiro回调的方法
 2     @Override
 3     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
 4         // 1.从PrincipalCollection中获取登入用户信息
 5         User user = (User) principalCollection.getPrimaryPrincipal();
 6         // 2.利用登入的用户信息来获取当前的角色或权限(可能需要查询数据库)
 7         Set<String> roles = new HashSet<>();
 8         roles.add("user");
 9         if ("Y".equals(user.getIsadmin())) {
10             roles.add("admin");
11         }
12         // 3.创建SimpleAuthorizationInfo,并设置其roles属性
13         SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
14         // 4.返回SimpleAuthorizationInfo对象
15         return simpleAuthorizationInfo;
16     }
View Code

index.jsp页面代码:

 1 <p>
 2 Welcom <shiro:principal></shiro:principal>
 3 </p>
 4 
 5 <input type="button" value="添加" id="add">
 6 <a href="/logout"><s:message code="SYS.LOGOUT"></s:message></a>
 7 &nbsp;&nbsp;&nbsp;
 8 <shiro:hasRole name="admin">
 9 <a href="/role/admin"><s:message code="SYS.ROLE.ADMIN"></s:message></a>
10 &nbsp;&nbsp;&nbsp;
11 </shiro:hasRole>
12 <shiro:hasRole name="user">
13 <a href="/role/user"><s:message code="SYS.ROLE.USER"></s:message></a>
14 &nbsp;&nbsp;&nbsp;
15 </shiro:hasRole>

完整的index.jsp代码:

  1 <%@ page language="java" contentType="text/html; charset=UTF-8"
  2     pageEncoding="UTF-8"%>
  3 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
  4 <%@taglib prefix="s" uri="http://www.springframework.org/tags"%>
  5 <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
  6 <!DOCTYPE html>
  7 <html>
  8 <head>
  9 <script type="text/javascript" src="/js/jquery.min.js"></script>
 10 <meta charset="UTF-8">
 11 <title><s:message code="EMPLOYEE.LIST"></s:message></title>
 12 </head>
 13 <body>
 14 <p>
 15 Welcom <shiro:principal></shiro:principal>
 16 </p>
 17 
 18 <input type="button" value="添加" id="add">
 19 <a href="/logout"><s:message code="SYS.LOGOUT"></s:message></a>
 20 &nbsp;&nbsp;&nbsp;
 21 <shiro:hasRole name="admin">
 22 <a href="/role/admin"><s:message code="SYS.ROLE.ADMIN"></s:message></a>
 23 &nbsp;&nbsp;&nbsp;
 24 </shiro:hasRole>
 25 <shiro:hasRole name="user">
 26 <a href="/role/user"><s:message code="SYS.ROLE.USER"></s:message></a>
 27 &nbsp;&nbsp;&nbsp;
 28 </shiro:hasRole>
 29 
 30 <a href="/emp/testAuth"><s:message code="SYS.ROLE.TESTAUTH"></s:message></a>
 31     <table>
 32         <tr>
 33             <td>id</td>
 34             <td><s:message code="EMPLOYEE.LASTNAME"></s:message> </td>
 35             <td><s:message code="EMPLOYEE.EMAIL"></s:message></td>
 36             <td><s:message code="EMPLOYEE.GENDER"></s:message></td>
 37             <td><s:message code="SYS.OPERATE"></s:message></td>
 38         </tr>
 39         <c:forEach var="item" items="${pages.list}">
 40             <tr class="row">
 41                 <td>${item.id}</td>
 42                 <td>${item.lastName}</td>
 43                 <td>${item.email}</td>
 44                 <td>${item.gender}</td>
 45                 <td > <input type="button" value="<s:message code='SYS.EDIT'></s:message> "  class="editRow">
 46                 <input type="hidden" value="${item.id}"/>
 47                 </td>
 48                 <td > <input type="button" value="<s:message code='SYS.DETAIL'></s:message> "  class="detailRow">
 49                 <input type="hidden" value="${item.id}"/>
 50                 </td>
 51                 
 52             </tr>
 53         </c:forEach>
 54     </table>
 55     <p>
 56         <c:if test="${pages.hasPreviousPage}">
 57             <input type="button" class="page" value="前一页"/>
 58             <input type="hidden" value="${pages.prePage}">
 59         </c:if>
 60         <c:forEach var="item" items="${pages.navigatepageNums}">
 61             <c:if test="${item==pages.pageNum}">
 62                 <input type="button" class="page" style="color: red" value="${item}">
 63             </c:if>
 64             <c:if test="${item!=pages.pageNum}">
 65                 <input type="button" class="page" value="${item}">
 66             </c:if>
 67             <input type="hidden" value="${item}">
 68         </c:forEach>
 69         <c:if test="${pages.hasNextPage}">
 70             <input type="button" class="page" value="下一页"/>
 71             <input type="hidden" value="${pages.nextPage}">
 72         </c:if>
 73         <input type="hidden" id="pageNum" value="${pages.pageNum}"> <input
 74             type="hidden" id="pageSize" value="${pages.pageSize}">
 75     </p>
 76 </body>
 77 <script>
 78     $(function() {
 79         $(".page").each(function() {
 80                             $(this).click(function() {
 81                                                 var pageNum = $(this).next().val();
 82                                                 var pageSize = 5;
 83                                                 window.location = "${pageContext.request.contextPath}/emp/list?pageNum="
 84                                                         + pageNum
 85                                                         + "&pageSize="
 86                                                         + pageSize;
 87                                             })
 88                         });
 89         $("#add").click(function(){
 90             window.location = "${pageContext.request.contextPath}/emp/add"
 91         })
 92         $(".editRow").each(function(){
 93             $(this).click(function(){
 94                 var id = $(this).next().val();
 95                 window.location = "edit/"+id
 96             })
 97         })
 98         $(".detailRow").each(function(){
 99             $(this).click(function(){
100                 var id = $(this).next().val();
101                 $.post("/emp/detail",{id:id},function(data){
102                     alert("姓名:"+data.lastName+",邮箱:"+data.email+",性别:"+data.gender);
103                 })
104                 
105             })
106         })
107         
108     })
109 </script>
110 </html>
View Code

权限注解:

1.在shiro.xml中需要配置:lifecycleBeanPostProcessor,在springmvc.xml中需要开启代理

shiro配置:

1 <bean
2         class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
3         depends-on="lifecycleBeanPostProcessor"/>
4 
5     <bean
6         class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
7         <property name="securityManager" ref="securityManager" />
8     </bean>

springMVC中的代理:

<aop:config proxy-target-class="true"></aop:config>

 

在EmployeeController中添加

1     @RequiresRoles({ "admin" })
2     @RequestMapping("/testAuth")
3     public String testAuth(HttpSession session) {
4         session.setAttribute("key", "value123");
5         employeeService.testAuth();
6         return "redirect:/emp/list";
7     }

在service中获取session

1 @Override
2     public void testAuth() {
3         Session session = SecurityUtils.getSubject().getSession();
4         String value = (String) session.getAttribute("key");// service 层也可以访问到session
5         System.out.println(new Date());
6         System.out.println("-------------->session value:" + value);
7     }

在页面点击 测试权限注解,如果有admin权限,则打印-------------->session value:value123;否则报错(Subject does not have role [admin]);

五、记住我的配置 rememberme

1、首先在登录页面选中 RememberMe 然后登录成功;如果是 浏览器登录,一般会把 RememberMe 的Cookie 写到客户端并 保存下来;

2、关闭浏览器再重新打开;会发现浏览器还是记住你的;

3、访问一般的网页服务器端还是知道你是谁,且能正常访问;

认证和记住我:

subject.isAuthenticated() 表示用户进行了身份验证登录的, 即使有 Subject.login 进行了登录;

subject.isRemembered():表示用户是通过记住我登录的, 此时可能并不是真正的你(如你的朋友使用你的电脑,或者 你的cookie 被窃取)在访问的 

两者二选一,即 subject.isAuthenticated()==true,则 subject.isRemembered()==false;反之一样

记住我的配置

1 <!-- 1.配置securityManager -->
2     <bean id="securityManager"
3         class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
4         <property name="cacheManager" ref="cacheManager" />
5         <!-- 配置单个realm -->
6         <property name="realm" ref="jdbcRealm" /> 
7        <property name="rememberMeManager.cookie.maxAge" value="10"></property>
8     </bean>

 login.jsp 添加了 记住我选项:

 1 <%@ page language="java" contentType="text/html; charset=UTF-8"
 2     pageEncoding="UTF-8"%>
 3 <!DOCTYPE html>
 4 <html>
 5 <head>
 6 <meta charset="UTF-8">
 7 <title>登入</title>
 8 <script type="text/javascript" src="/js/jquery.min.js"></script>
 9 </head>
10 <body>
11 <h4>Login page</h4>
12 
13 <form action="/login" method="post">
14 用户名:<input type="text" name="username" id="username"><br>
15 密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password" id="password"><br>
16 记住我:<input type="checkbox"  id="rememberme">
17 <input type="hidden" name="rememberme" value="off"><br>
18 <input type="submit" value="提交">
19 
20 </form>
21 </body>
22 <script>
23 $(function(){
24     $("#rememberme").on("change",function(){
25        var checkValue =  $(this).val();
26         $(this).next().val(checkValue);
27     })
28 })
29 </script>
30 </html>
View Code

 记住我 HomeControll  POST 的login  修改:

 1  @RequestMapping(value = "/login", method = RequestMethod.POST)
 2     public String login(@RequestParam(value = "username") String useranme,
 3             @RequestParam(value = "password") String password, @RequestParam(value = "rememberme") String rememberme) {
 4         Subject currentUser = SecurityUtils.getSubject();
 5         if (!currentUser.isAuthenticated()) {
 6             // 用户名和密码封装为UsernamePasswordToken对象
 7             UsernamePasswordToken token = new UsernamePasswordToken(useranme, password);
 8             // 设置记住我
 9             if (rememberme.equals("on")) {
10                 token.setRememberMe(true);
11             }
12             try {
13                 System.out.println(token.hashCode());
14                 // 执行登入
15                 currentUser.login(token);
16                 // 若没有指定的账户,则shiro将抛出UnknownAccountException
17             } catch (UnknownAccountException uae) {
18                 log.info("There is no user with username of " + token.getPrincipal());
19                 // 若账户存在,但密码不匹配,则shiro将抛出IncorrectCredentialsException
20             } catch (IncorrectCredentialsException ice) {
21                 log.info("Password for account " + token.getPrincipal() + " was incorrect!");
22                 // 若用户被锁定,抛出LockedAccountException
23             } catch (LockedAccountException lae) {
24                 log.info("The account for username " + token.getPrincipal() + " is locked.  "
25                         + "Please contact your administrator to unlock it.");
26             }
27             // 所有上面认证异常的父类
28             // ... catch more exceptions here (maybe custom ones specific to your
29             // application?
30             catch (AuthenticationException ae) {
31                 log.info("登入失败:" + ae.getMessage());
32             }
33         }
34         return "redirect:emp/list";
35     }
View Code

六、多Realm配置

实列:secondRealm.java,加密方式SHA1;

 1 package ssm.shiro;
 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.LockedAccountException;
 7 import org.apache.shiro.authc.SimpleAuthenticationInfo;
 8 import org.apache.shiro.authc.UnknownAccountException;
 9 import org.apache.shiro.authc.UsernamePasswordToken;
10 import org.apache.shiro.authz.AuthorizationInfo;
11 import org.apache.shiro.crypto.hash.SimpleHash;
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.beans.factory.annotation.Autowired;
16 
17 import ssm.entity.User;
18 import ssm.service.UserService;
19 
20 public class SecondShiroRealm extends AuthorizingRealm {
21     @Autowired
22     private UserService userService;
23 
24     @Override
25     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
26         System.out.println("SecondShiroRealm-doGetAuthenticationInfo:" + token.hashCode());
27         // 1.把AuthenticationToken 强转为usernamePasswordToken
28         UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
29         // 2.usernamePasswordTokenh获取username
30         String username = usernamePasswordToken.getUsername();
31         // 3.调用数据库方法,从数据库中查询username对应的用户记录
32         System.out.println("从数据库获取username:" + username + ",所对应的记录");
33         User user = userService.getByUser(username);
34         // 4.若用户不存在,则可以抛出UnknownAccountException异常;
35         if (user != null) {
36 
37             // 5.根据用户信息的情况,决定是否需要抛出其他的AuthenticcationException异常
38             if ("monster".equals(username)) {
39                 throw new LockedAccountException();
40             }
41             // 6.根据用户的情况,来构建AuthenticationInfo
42             // 以下信息时从数据库中获取的
43             // 1).principal:实体的认证信息,可以是username,也可以是数据表对应的用户的实体类对象
44             Object principal = user;
45             // 2)。credentials:密码(后台查询得到的密码)
46             Object credentials = user.getPassword();
47             // 3)。realmName:当前realm对象的name,调用父类getName()方法即可;
48             String realmName = getName();
49             // 4)。设置盐值
50             ByteSource salt = ByteSource.Util.bytes(username);
51             // SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
52             SimpleAuthenticationInfo info2 = new SimpleAuthenticationInfo(principal, credentials, salt, realmName);
53             return info2;
54         } else {
55             throw new UnknownAccountException();
56         }
57 
58     }
59 
60     public static void main(String[] args) {
61         String hashAlgorithmName = "SHA1";
62         Object credentials = "123456";
63         Object salt = ByteSource.Util.bytes("user");
64         int hashIterations = 1024;
65         SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
66         System.out.println(simpleHash);
67     }
68 
69     @Override
70     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
71         return null;
72     }
73 
74 }
View Code

配置信息:

多Realm 需要配置认证策略:

  • FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第 一个 Realm 身份验证成功的认证信息,其他的忽略;
  • AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和 FirstSuccessfulStrategy 不同,将返回所有Realm身份验证成功的认证信 息;
  • AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有 Realm身份验证成功的认证信息,如果有一个失败就失败了。
  • ModularRealmAuthenticator 默认是 AtLeastOneSuccessfulStrategy 策略
 1 <bean id="securityManager"
 2         class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
 3         <property name="cacheManager" ref="cacheManager" />
 4         <!-- 配置多个realm -->
 5         <property name="authenticator" ref="authenticator"></property>
 6         <property name="realms">
 7             <list>
 8                 <ref bean="jdbcRealm"></ref>
 9                 <ref bean="secondShiroRealm"></ref>
10             </list>
11         </property>
12        <property name="rememberMeManager.cookie.maxAge" value="10000"></property>
13     </bean>
14     
15     <bean id="authenticator"
16         class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
17         
18         <!-- 
19         
20         FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第 一个 Realm 身份验证成功的认证信息,其他的忽略;
21 • AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和 FirstSuccessfulStrategy 不同,将返回所有Realm身份验证成功的认证信 息;
22 • AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有 Realm身份验证成功的认证信息,如果有一个失败就失败了。
23 • ModularRealmAuthenticator 默认是 AtLeastOneSuccessfulStrategy 策略
24          -->
25         <property name="authenticationStrategy">
26             <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
27         </property>
28     </bean>
29     
30     <bean id="secondShiroRealm" class="ssm.shiro.SecondShiroRealm">
31         <property name="credentialsMatcher">
32             <bean
33                 class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
34                 <!-- 指定加密算法 -->
35                 <property name="hashAlgorithmName" value="SHA1"></property>
36                 <!-- 可以指定加密的次数 -->
37                 <property name="hashIterations" value="1024"></property>
38                 <property name="storedCredentialsHexEncoded" value="true"/>
39             </bean>
40         </property>
41     </bean>

 当登入 用户名为 user , 密码为 123456 时 也会登入成功;

附录:

表结构&数据:

tbl_user:

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80025
 Source Host           : localhost:3306
 Source Schema         : mybatis

 Target Server Type    : MySQL
 Target Server Version : 80025
 File Encoding         : 65001

 Date: 13/05/2022 11:11:50
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tbl_user
-- ----------------------------
DROP TABLE IF EXISTS `tbl_user`;
CREATE TABLE `tbl_user`  (
  `ID` int NOT NULL AUTO_INCREMENT,
  `NAME` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `PASSWORD` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `ISADMIN` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of tbl_user
-- ----------------------------
INSERT INTO `tbl_user` VALUES (1, 'joy', '02b7e99ffcb1145409f1b72da04b9f5f', 'Y');
INSERT INTO `tbl_user` VALUES (2, 'tom', '93c3ef4bc1b01b865028192eeda1a41d', 'N');
INSERT INTO `tbl_user` VALUES (3, 'user', '073d4c3ae812935f23cb3f2a71943f49e082a718', 'N');

SET FOREIGN_KEY_CHECKS = 1;
View Code

tbl_employee

 1 /*
 2  Navicat Premium Data Transfer
 3 
 4  Source Server         : localhost
 5  Source Server Type    : MySQL
 6  Source Server Version : 80025
 7  Source Host           : localhost:3306
 8  Source Schema         : mybatis
 9 
10  Target Server Type    : MySQL
11  Target Server Version : 80025
12  File Encoding         : 65001
13 
14  Date: 13/05/2022 11:11:33
15 */
16 
17 SET NAMES utf8mb4;
18 SET FOREIGN_KEY_CHECKS = 0;
19 
20 -- ----------------------------
21 -- Table structure for tbl_employee
22 -- ----------------------------
23 DROP TABLE IF EXISTS `tbl_employee`;
24 CREATE TABLE `tbl_employee`  (
25   `id` int NOT NULL AUTO_INCREMENT,
26   `d_id` int NULL DEFAULT NULL,
27   `last_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
28   `gender` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
29   `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
30   PRIMARY KEY (`id`) USING BTREE
31 ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
32 
33 -- ----------------------------
34 -- Records of tbl_employee
35 -- ----------------------------
36 INSERT INTO `tbl_employee` VALUES (1, 1, 'JoyLi', '女', '1602211058@qq.com');
37 
38 SET FOREIGN_KEY_CHECKS = 1;
View Code

 代码地址:

SSM(示例代码码云地址:https://gitee.com/joy521125/ssm-senior-base.git)

SSM的maven版本(示例代码码云地址:https://gitee.com/joy521125/ssm-senior.git);

查询ssm整合shiro 请查看shiro分支;

标签:username,springmvc,spring,SSM,admin,user,org,import,shiro
来源: https://www.cnblogs.com/lixiuming521125/p/16255298.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有