Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
preface
For Java Security frameworks, one is Spring Security and the other is Shiro. These two frameworks are very good, there is no absolute who is better or worse, depending on the business scenario to choose the framework, the most suitable is the best.
Today we’re going to focus on Shiro as a security framework that I’m sure you use a lot. As for why Shiro is so popular, I think it’s the simplicity and power of Shiro that has caught the attention of many developers out of all the permission frameworks.
Apache Shiro is a powerful and easy-to-use Java security framework that performs authentication, authorization, password, and session management. Use Shiro’s easy-to-understand API. You can quickly and easily get any application, from the smallest mobile applications to the largest network and enterprise applications.
To sum up, Shiro is simple, flexible and easy to use. As for the basic concepts, I think it’s easy to explain them step by step in the code.
The body of the
The process of Shiro login, authentication and authorization is as follows:
Springboot integrates with Shiro framework for push-button level permissions. Involving authority, which involves three tables of users, roles and permissions and two associated tables of user roles and role permissions. The database I use is the common MYSQL, here I simply designed the table structure, as follows.
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
`id` int(11) NOT NULL,
`available` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL,
`parent_ids` varchar(255) DEFAULT NULL,
`permission` varchar(255) DEFAULT NULL,
`resource_type` varchar(255) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL.PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES ('1'.'0'.'User Management'.'0'.'0 /'.'userInfo:view'.'menu'.'userInfo/userList');
INSERT INTO `sys_permission` VALUES ('2'.'0'.'Add user'.'1'.'0/1'.'userInfo:add'.'button'.'userInfo/userAdd');
INSERT INTO `sys_permission` VALUES ('3'.'0'.'Delete user'.'1'.'0/1'.'userInfo:del'.'button'.'userInfo/userDel');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL,
`available` int(11) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`role` varchar(255) DEFAULT NULL.PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1'.'0'.'Administrator'.'admin');
INSERT INTO `sys_role` VALUES ('2'.'0'.'VIP'.'vip');
INSERT INTO `sys_role` VALUES ('3'.'1'.'Tester'.'test');
-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (
`permission_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
KEY `FKomxrs8a388bknvhjokh440waq` (`permission_id`),
KEY `FK9q28ewrhntqeipl1t04kh1be7` (`role_id`),
CONSTRAINT `FK9q28ewrhntqeipl1t04kh1be7` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`),
CONSTRAINT `FKomxrs8a388bknvhjokh440waq` FOREIGN KEY (`permission_id`) REFERENCES `sys_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES ('1'.'1');
INSERT INTO `sys_role_permission` VALUES ('2'.'2');
INSERT INTO `sys_role_permission` VALUES ('3'.'2');
INSERT INTO `sys_role_permission` VALUES ('2'.'3');
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`uid` int(11) NOT NULL,
`username` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`salt` varchar(255) DEFAULT NULL,
`state` int(1) DEFAULT NULL.PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1'.'admin'.'Administrator'.'123456'.'8d78869f470951332959580424d4bf4f'.'0');
INSERT INTO `sys_user` VALUES ('2'.'jiangwang'.'vip'.'123456'.'8d78869f470951332959580424d4bf4f'.'0');
INSERT INTO `sys_user` VALUES ('3'.'test'.'test'.'123456'.'8d78869f470951332959580424d4bf4f'.'0');
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`role_id` int(11) DEFAULT NULL,
`uid` int(11) DEFAULT NULL,
KEY `FKhh52n8vd4ny9ff4x9fb8v65qx` (`role_id`),
KEY `FKgkmyslkrfeyn9ukmolvek8b8f` (`uid`),
CONSTRAINT `FKgkmyslkrfeyn9ukmolvek8b8f` FOREIGN KEY (`uid`) REFERENCES `sys_user` (`uid`),
CONSTRAINT `FKhh52n8vd4ny9ff4x9fb8v65qx` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1'.'1');
INSERT INTO `sys_user_role` VALUES ('1'.'2');
INSERT INTO `sys_user_role` VALUES ('2'.'2');
INSERT INTO `sys_user_role` VALUES ('1'.'3');
INSERT INTO `sys_user_role` VALUES ('3'.'1');
Copy the code
After the database design, the following is to write the code, the business logic is very simple, after the user login successfully, according to the user’s own role and display the permissions. Login must be authenticated
Create a project
The directory structure is as follows:
Once the project is created, dependencies need to be added. I use the MyBatis framework as the persistence layer to reverse-engineer the code.
Add the dependent
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.31.</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <! -- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<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.4. 0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.32.</version>
<configuration>
<overwrite>true</overwrite>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
</configuration>
</plugin>
</plugins>
</build>
Copy the code
After the basic code generation, the business code is written.
application.properties
server.port=7777
spring.datasource.url=jdbc:mysql:/ / 127.0.0.1:3306 / shiro_demo? serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.type-aliases-package=com.jw.model
mybatis.mapper-locations=classpath:mapping/*.xml
logging.level.tk.mybatis=TRACE
Copy the code
createUserService.java
file
@Service
public class UserService
{
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private SysUserRoleMapper sysUserRoleMapper;
@Autowired
private SysRoleMapper sysRoleMapper;
@Autowired
private SysRolePermissionMapper sysRolePermissionMapper;
@Autowired
private SysPermissionMapper sysPermissionMapper;
public List<SysUser> getList(int id)
{
SysUserExample example = new SysUserExample();
example.createCriteria().andUidEqualTo(id);
return sysUserMapper.selectByExample(example);
}
/** * Query user ** based on user name@paramUsername username *@returnUser * /
public SysUser findByUsername(String username)
{
SysUser user = new SysUser();
SysUserExample example = new SysUserExample();
example.createCriteria().andUsernameEqualTo(username);
List<SysUser> userList = sysUserMapper.selectByExample(example);
if (userList.isEmpty())
{
return null;
}
for (SysUser tbUser : userList)
{
user = tbUser;
}
return user;
}
/** * Query the user role **@paramId User ID *@returnUser role */
public List<SysRole> findRolesById(int id)
{
SysUser userInfo = sysUserMapper.selectByPrimaryKey(id);
if (userInfo == null)
{
throw new RuntimeException("This user does not exist");
}
List<SysRole> roles = new ArrayList<>();
SysUserRoleExample userRoleExample = new SysUserRoleExample();
userRoleExample.createCriteria().andUidEqualTo(userInfo.getUid());
List<SysUserRole> sysUserRoleList = sysUserRoleMapper.selectByExample(userRoleExample);
List<Integer> rids = new ArrayList<>();
if(! CollectionUtils.isEmpty(sysUserRoleList)) {for (SysUserRole sysUserRole : sysUserRoleList)
{
rids.add(sysUserRole.getRoleId());
}
if(! CollectionUtils.isEmpty(rids)) {for (Integer rid : rids)
{
SysRole sysRole = sysRoleMapper.selectByPrimaryKey(rid);
if(sysRole ! =null) { roles.add(sysRole); }}}}return roles;
}
/** * Query user permissions **@paramRoles User role *@returnUser permissions */
public List<SysPermission> findPermissionByRoles(List<SysRole> roles)
{
List<SysPermission> permissions = new ArrayList<>();
if(! CollectionUtils.isEmpty(roles)) { Set<Integer> permissionIds =new HashSet<>();// Save the menu id
List<SysRolePermission> sysRolePermissions;
for (SysRole role : roles)
{
SysRolePermissionExample sysRolePermissionExample = new SysRolePermissionExample();
sysRolePermissionExample.createCriteria().andRoleIdEqualTo(role.getId());
sysRolePermissions = sysRolePermissionMapper.selectByExample(sysRolePermissionExample);
if(! CollectionUtils.isEmpty(sysRolePermissions)) {for(SysRolePermission sysRolePermission : sysRolePermissions) { permissionIds.add(sysRolePermission.getPermissionId()); }}}if(! CollectionUtils.isEmpty(permissionIds)) {for (Integer permissionId : permissionIds)
{
SysPermission permission = sysPermissionMapper.selectByPrimaryKey(permissionId);
if(permission ! =null) { permissions.add(permission); }}}}returnpermissions; }}Copy the code
createUserController.java
file
@Controller
@RequestMapping("/userInfo")
public class UserController
{
@GetMapping("/userList")
public String getUserList(a)
{
return "userList";
}
@GetMapping("/userAdd")
public String addUser(a)
{
return "addUser";
}
@GetMapping("/userDel")
public String deleteUser(a)
{
return "deleteUser"; }}Copy the code
createLoginController.java
file
@Controller
public class LoginController
{
@GetMapping(value = "/toLogin")
public String toLogin(a)
{
return "login";
}
@PostMapping("/login")
public String login(
@RequestParam("username") String username,
@RequestParam("password") String password,
Model model)
{
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try
{
subject.login(token);
return "index";
}
catch (UnknownAccountException uae)
{
model.addAttribute("msg"."User does not exist");
return "login";
}
catch (IncorrectCredentialsException ice)
{
model.addAttribute("msg"."Incorrect password");
return "login"; }}@GetMapping("/logOut")
public String logOut(a)
{
return "login";
}
@GetMapping("/noAuthorization")
public String noAuthorization(a)
{
return "This page cannot be accessed without authorization"; }}Copy the code
createCurrentUser.java
file
@Data
public class CurrentUser
{
// Current login user
private SysUser userInfo;
// The role owned by the current user
private List<SysRole> roles;
// Permissions of the current user
private List<SysPermission> permissions;
}
Copy the code
createMyRealm.java
file
Inherit the Shirot framework’s AuthorizingRealm class and implement the default two methods:
public class MyRealm extends AuthorizingRealm
{
@Autowired
private UserService userService;
/** * authorized **@param principalCollection
* @return* /
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
{
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// Get the current user
CurrentUser currentUser = (CurrentUser) SecurityUtils.getSubject().getPrincipal();
List<SysRole> roles = currentUser.getRoles();
List<SysPermission> permissions = currentUser.getPermissions();
if(! CollectionUtils.isEmpty(roles)) {for (SysRole role : roles)
{
// Authorize the roleauthorizationInfo.addRole(role.getRole()); }}if(! CollectionUtils.isEmpty(permissions)) {for (SysPermission permission : permissions)
{
// Grant permissionsauthorizationInfo.addStringPermission(permission.getPermission()); }}return authorizationInfo;
}
/** * Authentication **@param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
{
// The current user name
String username = (String) token.getPrincipal();
SysUser user = userService.findByUsername(username);
if (user == null)
{
return null;
}
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
// Put the current user's information into session
session.setAttribute("user", user);
// Get the role of the current user
List<SysRole> roles = userService.findRolesById(user.getUid());
// Get the permissions of the current user
List<SysPermission> permissions = userService.findPermissionByRoles(roles);
CurrentUser currentUser = new CurrentUser();
currentUser.setUserInfo(user);
currentUser.setRoles(roles);
currentUser.setPermissions(permissions);
return newSimpleAuthenticationInfo(currentUser, user.getPassword(), getName()); }}Copy the code
createShiroConfig.java
file
@Configuration
public class ShiroConfig
{
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
{
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
HashMap<String, String> filterMap = new LinkedHashMap<>();
/ / authorization
filterMap.put("/userInfo/userAdd"."perms[userInfo:add]");
filterMap.put("/userInfo/userDel"."perms[userInfo:del]");
filterMap.put("/userInfo/userList"."perms[userInfo:view]");
// The URL to intercept
filterMap.put("/userInfo/*"."authc");
// Pages that do not need to be blocked
filterMap.put("/static/**"."anon");
// The blocked page redirects to the login page
bean.setLoginUrl("/toLogin");
// The link to jump to after successful login
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/noAuthorization");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(a)
{
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(myRealm());
return defaultWebSecurityManager;
}
@Bean
public MyRealm myRealm(a)
{
return new MyRealm();
}
@Bean
public ShiroDialect getShiroDialect(a)
{
return newShiroDialect(); }}Copy the code
Create the Templates folder in the Resources directory and create the following HTML files in that folder.
createindex.html
file
<! DOCTYPEhtml>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Home page</title>
</head>
<body>
<h1>Home page</h1>
<div shiro:hasPermission="userInfo:view">
<a th:href="@{/userInfo/userList}">Query the user</a>
</div>
<div shiro:hasPermission="userInfo:add">
<a th:href="@{/userInfo/userAdd}">Add user</a>
</div>
<div shiro:hasPermission="userInfo:del">
<a th:href="@{/userInfo/userDel}">Delete user</a>
</div>
</body>
</html>
Copy the code
createlogin.html
file
<! DOCTYPEhtml>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>The login</h1>
<hr>
<p th:text="${msg}"></p>
<form action="/login" method="post">
<p>User name:<input type="text" name="username"/></p>
<p>The secret code:<input type="text" name="password"/></p>
<p><input type="submit"/></p>
</form>
</body>
</html>
Copy the code
createuserList.html
file
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Query the user</title>
</head>
<body>
<p>User query</p>
</body>
</html>
Copy the code
createaddUser.html
file
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Add user</title>
</head>
<body>
<p>Add user</p>
</body>
</html>
Copy the code
createdeleteUser.html
file
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Delete user</title>
</head>
<body>
<p>Delete user</p>
</body>
</html>
Copy the code
Start the project
- access
http://localhost:7777/toLogin
, login page, enter user name and password, click Submit.
- As you can see, the admin user has the permission to view and add, but does not have the permission to delete
- Log in as another user and see what permissions you have.
- It can be seen that the Jiangwang user has the permission to view, add and delete.
Shiro encryption
The passwords we save in the database are in plain text. Once the database data is leaked, it will cause unestimable losses. Therefore, we usually use asymmetric encryption, which is simply understood as irreversible encryption. In order to be more secure, we use the method of adding salt + multiple encryption.
/** * Password encryption *@paramThe source code *@paramSalt salt *@return* /
public static String md5Encryption(String source, String salt)
{
String algorithmName = "MD5";// Encryption algorithm
int hashIterations = 1024;// Encryption times
SimpleHash simpleHash = new SimpleHash(algorithmName, source, salt, hashIterations);
return simpleHash + "";
}
Copy the code
summary
The application of permissions in our project is very extensive, involving permissions can include: Login permissions, menu access, data access permissions (button), the demo above you can see different user logged in, have different access permissions (data), our projects, involving permissions can have these a few tables, the user table, table, permissions list, users and roles is a many-to-many relationship, roles and permissions is a many-to-many relationship.
Complete code managed code cloud: gitee.com/jiangwang00…