Recently, I am working on a final work, which is to use SSM + Thymeleaf +vue+ Shiro to complete a project that has permission to log in and can add, delete, check and change user information. The following is just to realize permission authentication and login. The reason why I choose Shiro instead of Spring Security is that I have tried Security and it is really difficult to package it. Haha, so I give up and choose Shiro.
Here is a small Demo of learning Shiro:
1. First, the underlying database:
-- ----------------------------
-- Table structure for role
-- ----------------------------
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary key of role table',
`role_name` varchar(32) DEFAULT NULL COMMENT 'Role Name'.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 'User primary key',
`username` varchar(32) NOT NULL COMMENT 'Username',
`password` varchar(32) NOT NULL COMMENT 'password',
`role_id` int(11) DEFAULT NULL COMMENT 'Foreign key associated with role role table'.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 table primary key',
`permission_name` varchar(50) NOT NULL COMMENT 'Permission name',
`role_id` int(11) DEFAULT NULL COMMENT 'Foreign key associated with role role table'.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);
Copy the code
2. Create a Spring Boot project and build it using Maven
Create entity classes (User, Role, Permissions) :
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
// Set of roles corresponding to the user
private Role role;
}
Copy the code
Role:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
private Integer id;
private String roleName;
}
Copy the code
Permissions:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permissions {
private Integer id;
private String permissionName;
private Role role;
}
Copy the code
We need to know the relationship between the three entity classes, User and Role one to one, Role and Permissions one to one. Of course, we can also write it as many-to-many, which needs to change the database file and entity class.
Add dependencies to pom.xml:
Only the relevant dependent sources are given below
<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>
Copy the code
4. Integrate Mybatis with SpringBoot:
Just create a DAO layer, a service layer, and remember to add annotations:
<! DOCTYPEmapper 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>
<! Define a resultMap that encapsulates users and roles.
<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>
<! -- Configure to encapsulate the contents of 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>
<! Define a resultMap that encapsulates permission and roles.
<resultMap id="permissionRoleMap" type="com.example.csy.entity.Permissions">
<id property="id" column="id"/>
<result property="permissionName" column="permission_name"></result>
<! Configure the contents of the encapsulated Role -->
<association property="role" javaType="com.example.csy.entity.Role">
<id property="id" column="id"></id>
<! Column is the column name of the database -->
<result property="roleName" column="role_name"></result>
</association>
</resultMap>
</mapper>
Copy the code
(2) DAO layer:
@Mapper
public interface UserMapper {
User queryUserByUsername(@Param("username") String username);
Permissions queryPermissionByUsername(@Param("username") String username);
}
Copy the code
(3) Service layer:
@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) {
returnuserMapper.queryPermissionByUsername(username); }}Copy the code
At this point, our mybatis+ SpringBoot integration is almost complete, so test it in the test class:
@SpringBootTest
class CsyApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads(a) {
User admin = userMapper.queryUserByUsername("admin");
System.out.println(admin.toString());
Permissions permission = userMapper.queryPermissionByUsername("admin"); System.out.println(permission.toString()); }}Copy the code
Test result: The query result is obtained
6. Integrate Thymeleaf:
Front page:In the HTML page, we integrate Thymeleaf, use Jquery, semantic, need to guide package
Index.html code: in this case, User can only access A, Admin can access A,B, and superAdmin can access A,B, and C
<! DOCTYPEhtml>
<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>Information management platform - home page</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1"
/>
<title>Home page</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}">Home page</a>
<a class="active item" th:href="@{/about}">about</a>
<! -- Login and logout -->
<div class="right menu">
<! -- If not logged in -->
<! --<div shiro:authorize="! isAuthenticated()">-->
<div shiro:notAuthenticated="">
<a class="item" th:href="@{/toLoginPage}">
<i class="address card icon"></i>The login</a>
</div>
<! -- If you have logged in -->
<div shiro:authenticated="">
<a class="item">
<i class="address card icon"></i>User name:<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>The cancellation</a>
</div>
</div>
</div>
<div class="ui stackable three column grid">
<div class="column" shiro:hasAnyRoles="USER,ADMIN,SUPER_ADMIN"><! -- Have access to any one of the characters -->
<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>The night wind blows your gray hair on your temples<br/>To heal the scars of memory<br/>Your eyes are a mixture of light and shade<br/>Twilight covers your faltering steps<br/>Went into bed to hide the painting<br/>It's you talking with your head down<br/>I still wonder how big the world is</p>
</div>
<div class="column">
<p>I was also fond of childhood love words<br/>Not true or false do not struggle meaningless joke<br/>I will finally give her back my youth<br/>With the summer pop-up fingertips<br/>The heart moves away with the wind<br/>In the name of love would you still do it<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">The ideal is more than twenty years old</div>
<p>BWH_Steven</p>
</div>
</div>
</body>
</html>
Copy the code
The login. HTML code:
<! DOCTYPEhtml>
<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>User management System - Login</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>User management System - Login</h1>
<div class="ui container" style="margin-top: 180px;">
<div style="text-align: center; margin-bottom: 20px;">
<h1 class="header">The login</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="">The user name</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="">password</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>Remember the password</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>
Copy the code
success.html:
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>User Management System - Success</title>
</head>
<body>
<h2>Login successful</h2>
<a href="/index">Return to the home page</a>
</body>
</html>
Copy the code
7. Integrating Shiro into a project:
(1) Custom Realm:
We need to customize, authenticate and authorize:
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
/ * * *@MethodNameDoGetAuthorizationInfo Authorization operation *@DescriptionPermission configuration class *@Param [principalCollection]
* @Return AuthorizationInfo
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// Get the user name information
String username = (String) principalCollection.getPrimaryPrincipal();
// Create a simple authorization authentication information
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// Set the role information obtained from the role table for the user
authorizationInfo.addRole(userMapper.queryUserByUsername(username).getRole().getRoleName());
// Set the permission information obtained from the Permission table for this user
authorizationInfo.addStringPermission(userMapper.queryPermissionByUsername(username).getPermissionName());
return authorizationInfo;
}
/ * * *@MethodNameDoGetAuthenticationInfo Authentication *@DescriptionAuthentication configuration class *@Param [authenticationToken]
* @Return AuthenticationInfo
* @Author WangShiLin
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// Get the username based on the Token created in the receiving foreground
String username = (String) authenticationToken.getPrincipal();
// UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
// System.out.println(userToken.getPrincipal());
// System.out.println(userToken.getUsername());
// System.out.println(userToken.getPassword());
// Query related user information by user name (entity)
User user = userMapper.queryUserByUsername(username);
if(user ! =null) {
// Save to Session, optional
SecurityUtils.getSubject().getSession().setAttribute("user", user);
// Shiro does the password authentication
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), "userRealm");
return authenticationInfo;
} else {
// If null is returned, an exception is thrown
return null; }}}Copy the code
(2) write shiroConfig:
@Configuration
public class ShiroConfig {
// Add your own validation method to the container
@Bean
public UserRealm myShiroRealm(a) {
return new UserRealm();
}
/** * configures the SecurityManager SecurityManager **@return* /
@Bean
public DefaultWebSecurityManager securityManager(a) {
// Add custom realms
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
/ / associated Realm
securityManager.setRealm(myShiroRealm());
return securityManager;
}
/** * Configure Shiro filter **@param securityManager
* @return* /
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
/ / define shiroFactoryBean
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
/ / associated securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// Customize the login page, if the login time, the request will be executed, i.e. jump to the login page
shiroFilterFactoryBean.setLoginUrl("toLoginPage");
// Specify a success page
shiroFilterFactoryBean.setSuccessUrl("/success");
// Specify an unauthorized interface
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
// Set a custom filter
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("anyRoleFilter".new MyRolesAuthorizationFilter());
shiroFilterFactoryBean.setFilters(filterMap);
// LinkedHashMap is ordered for sequential interceptor configuration
Map<String, String> filterChainMap = new LinkedHashMap<>();
// Configure the IP address that can be accessed anonymously. You can add and permit static resources based on the actual situation. Anon indicates the permit
filterChainMap.put("/css/**"."anon");
filterChainMap.put("/img/**"."anon");
filterChainMap.put("/js/**"."anon");
// Specify page release, for example, the login page allows all users to log in
filterChainMap.put("/toLoginPage"."anon");
// Users starting with "/user/admin" need to be authenticated. Authc indicates authentication
filterChainMap.put("/user/admin/**"."authc");
// page - User needs role authentication
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]");
// / all requests under user/admin/ must be authenticated. Only the user:[*] permission can be accessed. You can also set the permission to user: XXX
filterChainMap.put("/user/admin/**"."perms[user:*]");
// Configure the logout filter
filterChainMap.put("/logout"."logout");
// Store the Map to the filter
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
return shiroFilterFactoryBean;
}
/** * integrate thymeleaf *@return* /
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(a){
return new ShiroDialect();
}
Copy the code
First we inject our custom Realm method dependency into the container
// Add your own validation method to the container
@Bean
public UserRealm myShiroRealm(a) {
return new UserRealm();
}
Copy the code
Then: SecurityManager configures the SecurityManager
/** * configures the SecurityManager SecurityManager **@return* /
@Bean
public DefaultWebSecurityManager securityManager(a) {
// Add custom realms
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
/ / associated Realm
securityManager.setRealm(myShiroRealm());
return securityManager;
}
Copy the code
Finally, there are custom filters that control what roles those pages need to access, what resources need to be accessed by whom, and setSecurityManager, which returns a ShiroFilterFactoryBean.
Key stores URL, value stores some corresponding permissions or roles, etc. In fact, the key block is very easy to understand, for example: / CSS/and /user/admin/ respectively represent all files in the CSS folder. The request path prefix is /user/admin/ URL, and the corresponding value has certain specifications.
Roles [XXX] : [XXX] : [XXX] : [XXX] : [XXX] : You must have the permission to access a request or resource. Note: XXX is the permission parameter
(3) a custom role authentication filter MyRolesAuthorizationFilter:
Because our roles need only one role to access the mapping page, Shiro defaults to hasAllRoles, which means we need to satisfy all identities to access it, so we need to customize a hasAnyRoles and just choose one of them.
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; }}Copy the code
(4) Finally, the controller
The Controller is the front-end controller of springMvc that receives any request and returns the corresponding page (map). First of all, we will write the mapping of all the pages,
PageController:
@Controller
public class PageController {
@RequestMapping({"/", "index"})
public String index(a) {
return "index";
}
@RequestMapping("about")
public String toAboutPage(a) {
return "redirect:http://www.ideal-20.cn";
}
@RequestMapping("/toLoginPage")
public String toLoginPage(a) {
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(a) {
return "views/unauthorized";
}
@RequestMapping("/success")
public String toSuccessPage(a) {
return "views/success"; }}Copy the code
UserController: The above two mappings are just tests, mainly the login method, which can create a token based on our foreground input. If the token can be authenticated, it will return a success page, otherwise it will fail.
@Controller
public class UserController {
@RequestMapping("/user/queryAll")
@ResponseBody
public String queryAll(a) {
return "This is the user/queryAll method";
}
@RequestMapping("/user/admin/add")
@ResponseBody
public String adminAdd(a) {
return "This is the user/adminAdd method";
}
@RequestMapping("/login")
public String login(String username, String password, HttpServletRequest request) {
// Since it is obtained from the name parameter, I encapsulate it here
User user = new User();
user.setUsername(username);
user.setPassword(password);
// Create a Token based on the foreground user name and password.
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// Get the subject authentication principal (in this case, the user currently logged in)
Subject subject = SecurityUtils.getSubject();
try{
// The authentication starts, and this jumps to the custom UserRealm
subject.login(token);
// Can be stored in session
request.getSession().setAttribute("user", user);
return "views/success";
}catch(Exception e){
// Catch an exception
e.printStackTrace();
request.getSession().setAttribute("user", user);
request.setAttribute("errorMsg"."Dude, wrong username or password.");
return "views/login"; }}}Copy the code
8. Final Effect:
The first is http://localhost:8080/index
Login interface:
When the form is submitted, it returns the value to the UserController Login method, which authenticates:
Now we are logged in successfully and have superAdmin permission to view A,B, and C
User John, on the other hand, can only see A
End here, this blog for reference: blog, need source please view this blog.