我是一个小白,到处百度加上自己自学过的,简单的做了一下,当然只是最基础的,而且也不是很完善,正常来说应该是有用户表,角色表,权限表,用户与角色关系表,角色与权限关系表。
我写的可能也就我自己看懂,不喜勿喷!!!
一、准备工作
在进行ssm集成Shiro之前,需要准备相关的数据库表结构。如:用户表,角色表,权限表,用户与角色关系表,角色与权限关系表。
我呢,就没有准备这么多,设计相对简单,只准备了用户表和角色表,然后把权限作为列放在角色表里,初步试一下水。
二、表结构
用户表【注意:这里登录时用的是"userCode",可以自己改成userName】
角色表
三、pom.xml文件中导入依赖
1、shiro依赖的jar包
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.7.0</version>
</dependency>
2、pom.xml文件具体内容
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Books-maneger</artifactId>
<version>1.0-SNAPSHOT</version>
<!--依赖:junit,数据库驱动,连接池,servlet,jsp,mybatis,mybatis-spring,spring-->
<dependencies>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.2</version>
</dependency>
<!--Junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!--数据库连接池 c3p0: dbcp-->
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!--Servlet - jsp-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<!--jsp标签库-->
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
<!--静态资源导出-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
四、web.xml中添加如下配置
1、这里的【shiroFilter】要和spring-shiro.xml中【】的id一致
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
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_4_0.xsd"
version="4.0" metadata-complete="true">
<welcome-file-list>
<welcome-file>/login.jsp</welcome-file>
</welcome-file-list>
<!-- <error-page>-->
<!-- <error-code>404</error-code>-->
<!-- <location>/index.jsp</location>-->
<!-- </error-page>-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置Shiro的集成-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
2、resources下的结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oRXvV9ip-1614762163098)(C:\Users\Admin\AppData\Roaming\Typora\typora-user-images\image-20210303160926849.png)]
我的是把它们都分开,然后在一起整合到applicationContext.xml中,
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:spring-dao.xml"/>
<import resource="classpath:spring-service.xml"/>
<import resource="classpath:spring-mvc.xml"/>
<import resource="classpath:spring-shiro.xml"/>
</beans>
五、创建spring-shiro.xml【命名不做要求】,添加如下配置
缓存相关没有配置
1、spring-shiro.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--声明UserRealm,需要自己创建-->
<bean id="userRealm" class="com.bo.realm.UserRealm"/>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!--配置SecurityManager-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--注入Realm-->
<property name="realm" ref="userRealm"></property>
</bean>
<!--配置Shiro的过滤器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!--注入安全管理器-->
<property name="securityManager" ref="securityManager"></property>
<!--配置未登录时的跳转页面,默认是跳转到webapp/login.jsp目录-->
<property name="loginUrl" value="/login.jsp"></property>
<!--注入未授权的跳转页面,当访问某个资源权限不够时跳转的页面-->
<property name="unauthorizedUrl" value="/error.jsp"></property>
<!--配置过滤器链-->
<property name="filterChainDefinitions">
<value>
<!-- 这里是用来配置需要过滤的路径,但我用来注解过滤 -->
</value>
</property>
</bean>
</beans>
2、在spring-mvc.xml中加入以下
<!-- 开启shiro注解支持 配置shiro的注解适配器-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
3、完整的spring-mvc.xml
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1、注解驱动-->
<mvc:annotation-driven/>
<!-- 2、静态资源过滤-->
<mvc:default-servlet-handler/>
<!-- 3、扫描包:controller-->
<context:component-scan base-package="com.bo.controller"/>
<!-- 4、视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 开启shiro注解支持 配置shiro的注解适配器-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 未授权异常,这里默认走的是视图解析器中配置的页面路径,所以不用加后缀-->
<prop key="org.apache.shiro.authz.UnauthorizedException">
/noPerm
</prop>
<prop key="org.apache.shiro.authz.UnauthenticatedException">
</prop>
</props>
</property>
</bean>
</beans>
六、Mapper
userService和roleService就不放了,看Mapper就知道了
1、userMapper 和 userMapper.xml
//得到登录的用户
public User getLoginUser(@Param("userCode") String userCode, @Param("userPassword") String userPassword);
<select id="getLoginUser" resultType="User">
select * from smbms_user
<where>
<if test="userCode != null">
and userCode=#{userCode}
</if>
<if test="userPassword != null">
and userPassword=#{userPassword}
</if>
</where>
</select>
2、roleMapper 和 roleMapper.xml
//获取用户的角色的权限
public Set<String> getPerms(@Param("id") Integer id);
<select id="getPerms" resultType="String">
select perms from smbms_user u,smbms_role r where u.id=#{id} and u.userRole=r.id
</select>
七、创建UserRealm
package com.bo.realm;
import com.bo.pojo.User;
import com.bo.service.RoleService;
import com.bo.service.UserService;
import com.bo.util.Constants;
import lombok.SneakyThrows;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.Set;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权==>AuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获取认证处的user,即当前登录的对象
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
/**
*
* //获取用户角色,这里我没做
* Set<String> roleNameSet = userService.getRoleName(user.getId());
* //添加
* info.addRoles(roleNameSet);
*/
//查询权限
try {
System.out.println("用户id:"+user.getId());
Set<String> permissionNameSet = roleService.getPerms(user.getId());
//因为一个用户有很多权限,拼接的 ,需要拆分放入shiro
Set<String> permissions = new HashSet<String>();
for (String name:permissionNameSet){
for (String permission : name.split(",")){
permissions.add(permission);
}
}
//添加角色和权限列表
info.addStringPermissions(permissions);
} catch (Exception e) {
e.printStackTrace();
}
return info;
}
//认证
@SneakyThrows
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证==>AuthenticationInfo");
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
String userCode = userToken.getUsername();
System.out.println(userCode);
String password = new String(userToken.getPassword());
System.out.println(password);
//链接数据库
User user = userService.login(userCode,null);//可输入密码可以不输入,
// 因为下面的【new SimpleAuthenticationInfo(user,user.getUserPassword(),"");】会自动做密码验证
if (user==null){
return null;
}
//登录成功返回一个session,放在这里不太好,密码未验证
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.setAttribute(Constants.USER_SESSION,user);
//密码认证,shiro做 //此处的user【第一个参数】可以被授权处获得
return new SimpleAuthenticationInfo(user,user.getUserPassword(),"");
}
}
八、LoginController
package com.bo.controller;
import com.bo.pojo.User;
import com.bo.service.UserService;
import com.bo.util.Constants;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
import java.sql.SQLException;
@Controller
public class LoginController {
@Autowired
@Qualifier("UserServiceImpl")
private UserService userService;
@RequestMapping("/login")
public String login(String userCode, String userPassword, Model model, HttpSession session) throws SQLException {
System.out.println("进入LoginController......");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(userCode,userPassword);
try {
subject.login(token);
System.out.println("yes...............");
User user = (User) SecurityUtils.getSubject().getSession().getAttribute(Constants.USER_SESSION);
System.out.println(user);
System.out.println(session.getAttribute(Constants.USER_SESSION));
model.addAttribute(Constants.USER_SESSION,session.getAttribute(Constants.USER_SESSION));
}catch (UnknownAccountException e){//用户名不存在
model.addAttribute("error","用户名错误");
return "../../login";
}catch (IncorrectCredentialsException e){//密码不存在,在UserRealm中自动验证
model.addAttribute("error","密码错误");
return "../../login";
}
return "frame";
}
}
这里的Constants.USER_SESSION是一个我设置的常量,可以改成自己想的字符串
9、注解权限,就放个方法上来
@RequestMapping("/toAddPage")
@RequiresPermissions("provider:add")
public String toAddPage(){
return "provider/provideradd";
}
这个方法是说,我在jsp页面点击添加按钮,会跳转到添加页面。加入@RequiresPermissions(“provider:add”)后,则会先去执行UserRealm中的授权方法,判断登录的这个角色有没有这个权限,有就进入添加页面,没有则提示无权限。
10、遇到的问题
1、没有执行UserRealm中的授权方法
这是因为一开始我没有在spring-mvc中加入以下配置
<!-- 开启shiro注解支持 配置shiro的注解适配器-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
2、如果这个登录用户没有权限的话,会报500错误,而不是根据以下配置类跳转页面
<!--注入未授权的跳转页面,当访问某个资源权限不够时跳转的页面-->
<property name="unauthorizedUrl" value="/error.jsp"></property>
这是因为,注解配置权限和在配置文件中配置权限路径不同,需要我们在spring-mvc中加入以下配置
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--未授权异常,这里默认走的是视图解析器中配置的页面路径,所以不用加后缀-->
<prop key="org.apache.shiro.authz.UnauthorizedException">
/noPerm
</prop>
<!--未经验证异常,这里根据自己需要加吧-->
<prop key="org.apache.shiro.authz.UnauthenticatedException">
</prop>
</props>
</property>
</bean>
roperty name=“unauthorizedUrl” value="/error.jsp">
#### 这是因为,注解配置权限和在配置文件中配置权限路径不同,需要我们在spring-mvc中加入以下配置
```xml
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--未授权异常,这里默认走的是视图解析器中配置的页面路径,所以不用加后缀-->
<prop key="org.apache.shiro.authz.UnauthorizedException">
/noPerm
</prop>
<!--未经验证异常,这里根据自己需要加吧-->
<prop key="org.apache.shiro.authz.UnauthenticatedException">
</prop>
</props>
</property>
</bean>