SpringBoot+shiro+mybatis+Thymeleaf实现权限登录

最近在做一个期末作品,就是使用ssm+thymeleaf+vue+shiro完成一个具有权限登录,且能实现用户信息增删查改的这么一个项目,下面仅仅是实现权限认证和登录。为什么我选shiro,而不选spring Security,是因为我试过,security实在是比较难,封装的太厉害了,哈哈哈哈,所以果断放弃,选择shiro进行。

下一篇还实现了增删查改,使用vue,但是没有前后端分离,博客链接

!github源码连接,需要请自行下载。

提示,这个项目已经有了增删查改,跟着下面的博客做,也能做出来页面跳转权限,但是没有增删查改。

以下是学习shiro的一个小Demo:

1.首先是底层数据库:

1
2
3
4
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
44
45
46
47
48
49
50
51
52
53
-- ----------------------------
-- Table structure for role
-- ----------------------------
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色表主键',
`role_name` varchar(32) DEFAULT NULL COMMENT '角色名称',
PRIMARY KEY (`id`)
);

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'SUPER_ADMIN');
INSERT INTO `role` VALUES (2, 'ADMIN');
INSERT INTO `role` VALUES (3, 'USER');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户主键',
`username` varchar(32) NOT NULL COMMENT '用户名',
`password` varchar(32) NOT NULL COMMENT '密码',
`role_id` int(11) DEFAULT NULL COMMENT '与role角色表联系的外键',
PRIMARY KEY (`id`),
CONSTRAINT `user_role_on_role_id` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
);

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'BWH_Steven', '666666', 1);
INSERT INTO `user` VALUES (2, 'admin', '666666', 2);
INSERT INTO `user` VALUES (3, 'zhangsan', '666666', 3);

-- ----------------------------
-- Table structure for permission
-- ----------------------------
CREATE TABLE `permission` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '权限表主键',
`permission_name` varchar(50) NOT NULL COMMENT '权限名',
`role_id` int(11) DEFAULT NULL COMMENT '与role角色表联系的外键',
PRIMARY KEY (`id`),
CONSTRAINT `permission_role_on_role_id` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
);

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, 'user:*', 1);
INSERT INTO `permission` VALUES (2, 'user:*', 2);
INSERT INTO `permission` VALUES (3, 'user:queryAll', 3);

2.创建spring boot项目,用maven构建

创建实体类(User,Role,Permissions):
User:

1
2
3
4
5
6
7
8
9
10
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
//用户对应的角色集合
private Role role;
}

Role:

1
2
3
4
5
6
7
8
9
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
private Integer id;
private String roleName;


}

Permissions:

1
2
3
4
5
6
7
8
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permissions {
private Integer id;
private String permissionName;
private Role role;
}

我们需要知道三个实体类之间的关系,User与Role一对一,Role与Permissions一对一,当然也可以把它都写成多对多,这就需要去更改数据库文件,和实体类了。

3.在pom.xml添加相关依赖:

下面只给出相关依赖源

1
2
3
4
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
44
45
46
47
48
49
50
51
52
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>

4.整合mybatis和springboot:

就只需要创建一个dao层,一个服务层,需要记住要添加注解
(1)mapper配置文件(也可以使用注解形式):

1
2
3
4
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
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.csy.dao.UserMapper">

<select id="queryUserByUsername" resultMap="userRoleMap">
SELECT u.*,r.role_name FROM `user` u, `role` r
WHERE username = #{username} AND u.role_id = r.id;
</select>
<!-- 定义封装 User和 role 的 resultMap -->
<resultMap id="userRoleMap" type="com.example.csy.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<!-- 配置封装 UserPojo 的内容 -->
<association property="role" javaType="com.example.csy.entity.Role">
<id property="id" column="id"></id>
<result property="roleName" column="role_name"></result>
</association>
</resultMap>


<select id="queryPermissionByUsername" resultMap="permissionRoleMap">
SELECT p.* ,r.role_name FROM `user` u, `role` r, `permission` p
WHERE username = #{username} AND u.role_id = r.id AND p.role_id = r.id;
</select>
<!-- 定义封装 permission 和 role 的 resultMap -->
<resultMap id="permissionRoleMap" type="com.example.csy.entity.Permissions">
<id property="id" column="id"/>
<result property="permissionName" column="permission_name"></result>
<!-- 配置封装 Role 的内容 -->
<association property="role" javaType="com.example.csy.entity.Role">
<id property="id" column="id"></id>
<!--property是实体类中被赋值的参数名,column是数据库的列名-->
<result property="roleName" column="role_name"></result>
</association>
</resultMap>
</mapper>

(2)DAO层:

1
2
3
4
5
6
@Mapper
public interface UserMapper {
User queryUserByUsername(@Param("username") String username);

Permissions queryPermissionByUsername(@Param("username") String username);
}

(3)service层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;

@Override
public User queryUserByUsername(String username) {

return userMapper.queryUserByUsername(username);
}

@Override
public Permissions queryPermissionByUsername(String username) {
return userMapper.queryPermissionByUsername(username);
}
}

弄到这里,我们的mybatis+springboot整合也基本结束,所以在测试类里测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootTest
class CsyApplicationTests {

@Autowired
private UserMapper userMapper;

@Test
void contextLoads() {
User admin = userMapper.queryUserByUsername("admin");
System.out.println(admin.toString());
Permissions permission = userMapper.queryPermissionByUsername("admin");
System.out.println(permission.toString());
}
}

测试结果:
得到了查询结果

QQ截图20201120111727.png

6.整合Thymeleaf进来:

前端页面:
在html页面我们整合了Thymeleaf,使用了Jquery,semantic,需要导包
QQ截图20201120131847.png
QQ截图20201120131359.png

index.html代码:
在这里,如果是User就只能访问A,Admin能访问A,B,superAdmin能访问A,B,C

1
2
3
4
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<!DOCTYPE html>
<html lang="zh_CN"
xmlns:th="http://www.thymeleaf.org"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>信息管理平台-首页</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1"
/>
<title>首页</title>
<!--semantic-ui-->
<link
href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css"
rel="stylesheet"
/>

<!--<link href="css/index.css" rel="stylesheet">-->
<link th:href="@{/css/index.css}" rel="stylesheet">

<!-- <script th:src="@{js/jquery-3.1.1.min.js}"></script> -->
<script src="js/jquery-3.1.1.min.js"></script>
</head>
<body>
<div class="ui container">
<div class="ui secondary menu">
<a class="active item" th:href="@{/index}">
首页
</a>

<a class="active item" th:href="@{/about}">
关于
</a>
<!--登录注销-->
<div class="right menu">

<!--如果未登录-->
<!--<div shiro:authorize="!isAuthenticated()">-->
<div shiro:notAuthenticated="">
<a class="item" th:href="@{/toLoginPage}">
<i class="address card icon"></i> 登录
</a>
</div>

<!--如果已登录-->
<div shiro:authenticated="">
<a class="item">
<i class="address card icon"></i>
用户名:<span shiro:principal></span>
<!--角色:<span sec:authentication="principal.authorities"></span>-->
</a>
</div>

<div shiro:authenticated="">
<a class="item" th:href="@{/logout}">
<i class="address card icon"></i> 注销
</a>
</div>
</div>
</div>

<div class="ui stackable three column grid">
<div class="column" shiro:hasAnyRoles="USER,ADMIN,SUPER_ADMIN"><!--有其中任一一个角色课访问-->
<div class="ui raised segments">
<div class="ui segment">
<a th:href="@{/levelA/a}">L-A-a</a>
</div>
<div class="ui segment">
<a th:href="@{/levelA/b}">L-A-b</a>
</div>
<div class="ui segment">
<a th:href="@{/levelA/c}">L-A-c</a>
</div>
</div>
</div>
<div class="column" shiro:hasAnyRoles="ADMIN,SUPER_ADMIN">
<div class="ui raised segments">
<div class="ui segment">
<a th:href="@{/levelB/a}">L-B-a</a>
</div>
<div class="ui segment">
<a th:href="@{/levelB/b}">L-B-b</a>
</div>
<div class="ui segment">
<a th:href="@{/levelB/c}">L-B-c</a>
</div>
</div>
</div>
<div class="column" shiro:hasRole="SUPER_ADMIN">
<div class="ui raised segments">
<div class="ui segment">
<a th:href="@{/levelC/a}">L-C-a</a>
</div>
<div class="ui segment">
<a th:href="@{/levelC/b}">L-C-b</a>
</div>
<div class="ui segment">
<a th:href="@{/levelC/c}">L-C-c</a>
</div>
</div>
</div>
<!-- <div class="column"></div> -->
</div>

<div class="ui stacked segment">
<div class="ui stackable three column grid">
<div class="column">
<p>
晚风吹起你鬓间的白发<br/>
抚平回忆留下的疤<br/>
你的眼中 明暗交杂 一笑生花<br/>
暮色遮住你蹒跚的步伐<br/>
走进床头藏起的画<br/>
画中的你 低着头说话<br/>
我仍感叹于世界之大
</p>
</div>

<div class="column">
<p>

也沉醉于儿时情话<br/>
不剩真假 不做挣扎 无谓笑话<br/>
我终将青春还给了她<br/>
连同指尖弹出的盛夏<br/>
心之所动 就随风去了<br/>
以爱之名 你还愿意吗<br/>

</p>
</div>
<div class="column">
<img class="ui medium circular image" src="images/001.jpg">
</div>
</div>

</div>

<div class="ui info message">
<div class="header">理想二旬不止</div>
<p>BWH_Steven</p>
</div>
</div>

</body>
</html>

login.html代码:

1
2
3
4
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
>
<head>
<meta charset="UTF-8">
<title>用户管理系统-登录</title>
<!-- <script th:src="@{js/jquery-3.1.1.min.js}"></script> -->
<script src="js/jquery-3.1.1.min.js"></script>
<link
href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css"
rel="stylesheet"
/>
</head>
<body>
<h1>用户管理系统-登录</h1>
<div class="ui container" style="margin-top: 180px;">
<div style="text-align: center; margin-bottom: 20px;">
<h1 class="header">
登录
</h1>
</div>

<div class="ui three column stackable grid login-div">
<div class="column"></div>
<div class="column">
<form id="login" class="ui fluid form segment" th:action="@{/login}" method="post">
<div class="field">
<label class="">用户名</label>
<div class="ui left icon input">
<input type="text" name="username" placeholder=""/>
<i class="user icon"></i>
<div class="ui corner label">
<i class="icon asterisk"></i>
</div>
</div>
</div>
<div class="field">
<label class="">密码</label>
<div class="ui left icon input">
<input type="password" name="password" placeholder=""/>
<i class="lock icon"></i>
<div class="ui corner label">
<i class="icon asterisk"></i>
</div>
</div>
</div>
<div class="inline field">
<div class="ui checkbox">
<input type="checkbox" name="terms"/>
<label>记住密码</label>
</div>
</div>
<div class="inline field">
<input type="submit" class="ui blue submit button">
</div>
</form>
</div>
<div class="column"></div>
</div>
</div>


</body>
</html>

success.html:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理系统-成功</title>
</head>
<body>
<h2>登录成功</h2>
<a href="/index">返回主页</a>
</body>
</html>

7.将shiro整合到项目里:

(1)自定义Realm:

我们需要自定义,认证和授权:

1
2
3
4
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
44
45
46
47
48
49
50
51
52
53
54
55
public class UserRealm extends AuthorizingRealm {

@Autowired
private UserMapper userMapper;

/**
* @MethodName doGetAuthorizationInfo 授权操作
* @Description 权限配置类
* @Param [principalCollection]
* @Return AuthorizationInfo
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取用户名信息
String username = (String) principalCollection.getPrimaryPrincipal();
// 创建一个简单授权验证信息
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 给这个用户设置从 role 表获取到的角色信息
authorizationInfo.addRole(userMapper.queryUserByUsername(username).getRole().getRoleName());
//给这个用户设置从 permission 表获取的权限信息
authorizationInfo.addStringPermission(userMapper.queryPermissionByUsername(username).getPermissionName());
return authorizationInfo;
}

/**
* @MethodName doGetAuthenticationInfo 身份验证
* @Description 认证配置类
* @Param [authenticationToken]
* @Return AuthenticationInfo
* @Author WangShiLin
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 根据在接受前台数据创建的 Token 获取用户名
String username = (String) authenticationToken.getPrincipal();

// UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
// System.out.println(userToken.getPrincipal());
// System.out.println(userToken.getUsername());
// System.out.println(userToken.getPassword());

// 通过用户名查询相关的用户信息(实体)
User user = userMapper.queryUserByUsername(username);
if (user != null) {
// 存入 Session,可选
SecurityUtils.getSubject().getSession().setAttribute("user", user);
// 密码认证的工作,Shiro 来做
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), "userRealm");
return authenticationInfo;
} else {
// 返回 null 即会抛异常
return null;
}
}
}

(2)写配置类shiroConfig:

1
2
3
4
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@Configuration
public class ShiroConfig {

//将自己的验证方式加入容器
@Bean
public UserRealm myShiroRealm() {
return new UserRealm();
}

/**
* 配置安全管理器 SecurityManager
*
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager() {
// 将自定义 Realm 加进来
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 关联 Realm
securityManager.setRealm(myShiroRealm());
return securityManager;
}

/**
* 配置 Shiro 过滤器
*
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
// 定义 shiroFactoryBean
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

// 关联 securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);

// 自定义登录页面,如果登录的时候,就会执行这个请求,即跳转到登录页
shiroFilterFactoryBean.setLoginUrl("toLoginPage");
// 指定成功页面
shiroFilterFactoryBean.setSuccessUrl("/success");
// 指定未授权界面
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

// 设置自定义 filter
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("anyRoleFilter", new MyRolesAuthorizationFilter());
shiroFilterFactoryBean.setFilters(filterMap);

// LinkedHashMap 是有序的,进行顺序拦截器配置
Map<String, String> filterChainMap = new LinkedHashMap<>();

// 配置可以匿名访问的地址,可以根据实际情况自己添加,放行一些静态资源等,anon 表示放行
filterChainMap.put("/css/**", "anon");
filterChainMap.put("/img/**", "anon");
filterChainMap.put("/js/**", "anon");
// 指定页面放行,例如登录页面允许所有人登录
filterChainMap.put("/toLoginPage", "anon");

// 以“/user/admin” 开头的用户需要身份认证,authc 表示要进行身份认证
filterChainMap.put("/user/admin/**", "authc");

// 页面 -用户需要角色认证
filterChainMap.put("/levelA/**", "anyRoleFilter[USER,ADMIN,SUPER_ADMIN]");
filterChainMap.put("/levelB/**", "anyRoleFilter[ADMIN,SUPER_ADMIN]");
filterChainMap.put("/levelC/**", "anyRoleFilter[SUPER_ADMIN]");

// filterChainMap.put("/levelA/**", "roles[USER]");
// filterChainMap.put("/levelB/**", "roles[ADMIN]");
// filterChainMap.put("/levelC/**", "roles[SUPER_ADMIN]");

// /user/admin/ 下的所有请求都要经过权限认证,只有权限为 user:[*] 的可以访问,也可以具体设置到 user:xxx
filterChainMap.put("/user/admin/**", "perms[user:*]");

// 配置注销过滤器
filterChainMap.put("/logout", "logout");

// 将Map 存入过滤器
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
return shiroFilterFactoryBean;
}

/**
* 整合 thymeleaf
* @return
*/
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
return new ShiroDialect();

}

首先我们将自定义的Realm方法,依赖注入进来到容器

1
2
3
4
5
//将自己的验证方式加入容器
@Bean
public UserRealm myShiroRealm() {
return new UserRealm();
}

然后是:SecurityManager配置安全管理器

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 配置安全管理器 SecurityManager
*
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager() {
// 将自定义 Realm 加进来
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 关联 Realm
securityManager.setRealm(myShiroRealm());
return securityManager;
}

最后就是自定义的过滤器,控制那些页面需要什么样的角色才能访问,哪些资源需要谁才能访问,并且setSecurityManager,返回一个ShiroFilterFactoryBean。

重点说一下拦截放行(Map)这块:通过 map 键值对的形式存储,key 存储 URL ,value 存储对应的一些权限或者角色等等,其实 key 这块还是很好理解的,例如 :/css/、/user/admin/ 分别代表 css 文件夹下的所有文件,以及请求路径前缀为 /user/admin/ URL,而对应的 value 就有一定的规范了。

关键:
anon:无需认证,即可访问,也就是游客也可以访问
authc:必须认证,才能访问,也就是例如需要登录后
roles[xxx] :比如拥有某种角色身份才能访问 ,注:xxx为角色参数
perms[xxx]:必须拥有对某个请求、资源的相关权限才能访问,注:xxx为权限参数

(3)自定义一个角色认证过滤器MyRolesAuthorizationFilter:

因为我们的角色,只需用有一个角色就能访问到映射页面,shiro默认是hasAllRoles,也就是说,我们要满足所有的身份才能访问,所以需要我们自定义一个hasAnyRoles,任选其一角色即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MyRolesAuthorizationFilter extends AuthorizationFilter {

@SuppressWarnings({"unchecked"})
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {

Subject subject = getSubject(request, response);
String[] rolesArray = (String[]) mappedValue;

if (rolesArray == null || rolesArray.length == 0) {
//no roles specified, so nothing to check - allow access.
return false;
}

List<String> roles = CollectionUtils.asList(rolesArray);
boolean[] hasRoles = subject.hasRoles(roles);
for (boolean hasRole : hasRoles) {
if (hasRole) {
return true;
}
}
return false;
}
}

(4)最后就是controller

controller是springMvc的前端控制器,接收什么请求,并且返回对应指定的页面(映射)。
首先我们先将所以页面的映射写好,

PageController:

1
2
3
4
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
44
@Controller
public class PageController {

@RequestMapping({"/", "index"})
public String index() {
return "index";
}

@RequestMapping("about")
public String toAboutPage() {
return "redirect:http://www.ideal-20.cn";
}

@RequestMapping("/toLoginPage")
public String toLoginPage() {
return "views/login";
}

@RequestMapping("/levelA/{name}")
public String toLevelAPage(@PathVariable("name") String name) {
return "views/L-A/" + name;
}

@RequestMapping("/levelB/{name}")
public String toLevelBPage(@PathVariable("name") String name) {
return "views/L-B/" + name;
}

@RequestMapping("/levelC/{name}")
public String toLevelCPage(@PathVariable("name") String name) {
return "views/L-C/" + name;
}

@RequestMapping("/unauthorized")
public String toUnauthorizedPage() {
return "views/unauthorized";
}

@RequestMapping("/success")
public String toSuccessPage() {
return "views/success";
}

}

UserController:
上面那两个映射,只是测试,主要是那个login方法,他可以根据我们前台输入的数据,并创建一个token,如果该token能被认证,即返回成功页面,否则就失败。

1
2
3
4
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
@Controller
public class UserController {
@RequestMapping("/user/queryAll")
@ResponseBody
public String queryAll() {
return "这是 user/queryAll 方法";
}

@RequestMapping("/user/admin/add")
@ResponseBody
public String adminAdd() {
return "这是 user/adminAdd 方法";
}

@RequestMapping("/login")
public String login(String username, String password, HttpServletRequest request) {
// 由于是根据name参数获取的,我这里封装了一下
User user = new User();
user.setUsername(username);
user.setPassword(password);

// 创建出一个 Token 内容本质基于前台的用户名和密码(不一定正确)
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// 获取 subject 认证主体(这里也就是现在登录的用户)
Subject subject = SecurityUtils.getSubject();
try{
// 认证开始,这里会跳转到自定义的 UserRealm 中
subject.login(token);
// 可以存储到 session 中
request.getSession().setAttribute("user", user);
return "views/success";
}catch(Exception e){
// 捕获异常
e.printStackTrace();
request.getSession().setAttribute("user", user);
request.setAttribute("errorMsg", "兄弟,用户名或密码错误");
return "views/login";
}
}
}

8.最终效果:

首先是http://localhost:8080/index

QQ截图20201120134737.png

登录界面:

QQ截图20201120134930.png

表单提交后,就返回值到UserController那个Login方法,认证:

QQ截图20201120135024.png

这样我们就登录成功了,并且是superAdmin的权限,可以查看A,B,C

QQ截图20201120135156.png

而用户张三,只能看见A
QQ截图20201120135305.png

到此结束,本博客借鉴:博客,需要源码的请查看此博客。

评论