For space reasons, please refer to this article to realize the login function first https://blog.csdn.net/grd_java/article/details/121925792 |
---|
1, Database tables, and entity classes
Because the front end is written by VUE, components and paths need to be saved |
---|
- sql statements for all libraries
2, Menu management TODO
final result
1. Entity class
Through the mybatis plus plug-in code generator, it is automatically generated, and then a field children is added
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableField; import java.io.Serializable; import java.util.List; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; /** * <p> * * </p> * * @author testjava * @since 2021-12-14 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @ApiModel(value="DdMenu object", description="") public class DdMenu implements Serializable { private static final long serialVersionUID=1L; @ApiModelProperty(value = "id") @TableId(value = "id", type = IdType.AUTO) private Integer id; @ApiModelProperty(value = "Parent menu id") private Integer pid; @ApiModelProperty(value = "menu name") private String name; @ApiModelProperty(value = "Menu type (catalog, button)") private String type; @ApiModelProperty(value = "Menu permissions") private String permission; @ApiModelProperty(value = "url") private String url; @ApiModelProperty(value = "path") private String path; @ApiModelProperty(value = "assembly") private String component; @ApiModelProperty(value = "Icon") @TableField("iconCls") private String iconCls; @ApiModelProperty(value = "Keep active") @TableField("keepAlive") private Boolean keepAlive; @ApiModelProperty(value = "Permission required") @TableField("requireAuth") private Boolean requireAuth; @ApiModelProperty(value = "Enable") private Boolean enabled; @ApiModelProperty(value = "Submenu") @TableField(exist = false)//Tell Mybatis that this field does not exist in the table, otherwise it will be found in the database during operation private List<DdMenu> children; }
2. Redis cache
- Start redis
- Add dependency
<!--redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--commons-pool2 Object pool dependency--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
- redis configuration
spring: redis: #redis configuration host: 127.0.0.1 #Your redis address port: 6379 #Port number database: 0 timeout: 1800000 lettuce: pool: max-active: 1024 # maximum connection max-wait: -1 #Maximum blocking waiting time (negative number indicates no limit) max-idle: 200 #Maximum idle connections min-idle: 5 #Minimum idle connection
- Redis configuration class
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * redis Configuration class */ @Configuration public class RedisConfig { @Bean public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); //String type key sequencer redisTemplate.setKeySerializer(new StringRedisSerializer()); //String type value sequencer redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); //Hash type key sequencer redisTemplate.setHashKeySerializer(new StringRedisSerializer()); //Hash type value sequencer redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); //Configuration factory redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate; } }
3. Write logic and query menu
- controller
import com.dd.security.entity.DdMenu; import com.dd.security.service.DdMenuService; import com.dd.security.service.DdUserService; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * <p> * Front end controller * </p> * * @author testjava * @since 2021-12-14 */ @RestController @RequestMapping("/security/dd-menu") public class DdMenuController { @Autowired private DdMenuService ddMenuService; @ApiOperation(value = "By user id Query menu list") @GetMapping("/menu") public List<DdMenu> getMenusByUserId(){ return ddMenuService.getMenusByUserId(); } }
- service
import com.dd.security.entity.DdMenu; import com.dd.security.entity.DdUser; import com.dd.security.mapper.DdMenuMapper; import com.dd.security.service.DdMenuService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.awt.*; import java.util.List; /** * <p> * Service implementation class * </p> * * @author testjava * @since 2021-12-14 */ @Service public class DdMenuServiceImpl extends ServiceImpl<DdMenuMapper, DdMenu> implements DdMenuService { @Autowired private DdMenuMapper ddMenuMapper; @Autowired private RedisTemplate redisTemplate; /** * Get menu based on user id */ @Override public List<DdMenu> getMenusByUserId() { //Get UserDetails object from Security global context DdUser user = (DdUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); //Get redis object ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); //First query from redis List<DdMenu> menus = (List<DdMenu>) valueOperations.get("menu_" + user.getId()); //If not in redis, query the mysql database if(CollectionUtils.isEmpty(menus)){ menus = ddMenuMapper.getMenusByUserId(user.getId()); //Put the queried content in reids valueOperations.set("menu_" + user.getId(),menus); } //Return results return menus; } }
- mapper
select distinct m1.*, m2.id as id2, m2.pid as pid2, m2.`name` as name2, m2.type as type2, m2.permission as permission2, m2.url as url2, m2.path as path2, m2.component as component2, m2.iconCls as iconCls2, m2.keepAlive as keepAlive2, m2.requireAuth as requireAuth, m2.enabled as enabled2 from dd_menu as m1 inner join dd_menu as m2 #Self correlation on m1.id = m2.pid inner join dd_role_menu as rm on rm.mid = m2.id inner join dd_user_role as ur on ur.rid = rm.rid where ur.uid = 1
<?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.dd.security.mapper.DdMenuMapper"> <!-- DdMenu Mapping object--> <resultMap id="BaseResultMap" type="com.dd.security.entity.DdMenu"> <id column="id" property="id" /> <result column="pid" property="pid" /> <result column="name" property="name" /> <result column="type" property="type" /> <result column="permission" property="permission" /> <result column="url" property="url" /> <result column="path" property="path" /> <result column="component" property="component" /> <result column="iconCls" property="iconCls" /> <result column="keepAlive" property="keepAlive" /> <result column="requireAuth" property="requireAuth" /> <result column="enabled" property="enabled" /> </resultMap> <!--Submenu, inherit above DdMenu Mapping object--> <resultMap id="Menus" type="com.dd.security.entity.DdMenu" extends="BaseResultMap"> <collection property="children" ofType="com.dd.security.entity.DdMenu"> <id column="id2" property="id" /> <result column="pid2" property="pid" /> <result column="name2" property="name" /> <result column="type2" property="type" /> <result column="permission2" property="permission" /> <result column="url2" property="url" /> <result column="path2" property="path" /> <result column="component2" property="component" /> <result column="iconCls2" property="iconCls" /> <result column="keepAlive2" property="keepAlive" /> <result column="requireAuth2" property="requireAuth" /> <result column="enabled2" property="enabled" /> </collection> </resultMap> <select id="getMenusByUserId" resultMap="Menus"> select distinct m1.*, m2.id as id2, m2.pid as pid2, m2.`name` as name2, m2.type as type2, m2.permission as permission2, m2.url as url2, m2.path as path2, m2.component as component2, m2.iconCls as iconCls2, m2.keepAlive as keepAlive2, m2.requireAuth as requireAuth, m2.enabled as enabled2 from dd_menu as m1 inner join dd_menu as m2 on m1.id = m2.pid inner join dd_role_menu as rm on rm.mid = m2.id inner join dd_user_role as ur on ur.rid = rm.rid where ur.uid = #{id} </select> </mapper>
2, url role permissions
If we only control the menu and the user logs in to the management system, he can only see the menu corresponding to his role permissions. However, if the user obtains the url corresponding to our background interface by some means, even if the front end can't see the corresponding menu and button, he can't access the back-end interface through events. You can also directly enter the url through the browser address bar, because it has been logged in. At this time, you can directly access the url. Although we don't want him to have access to these interfaces, so that he can't see the corresponding menus and buttons, he can only watch him get data at this time |
---|
How to deal with this situation? Our menu table has a url field that records the url address |
---|
- In the menu entity class, we add the roles and permission attributes required to access this url and menu
- It is specified that the URLs in the menu table need specific roles and permissions to access. The remaining URLs can be allowed to have no roles or LOGIN_ROLE user access to the default role.
- When the user accesses the back end, first filter the request and judge whether the user requests the url of the menu table. If so, judge whether the user has the corresponding permission. If the user has no permission to intercept the request, the return permission is insufficient. If it is not a menu table url, add the default role login for it_ Role role
Final effect |
---|
Role required for access_ The path of the admin role can be accessed normally
Insufficient permission to access URLs requiring other roles
Accessing paths that do not require authorization will not trigger filtering, and will not intercept the permissions required to determine the url
1. url permissions: set the url accessed by the user in the global. What permissions are required
1. Obtain the role permissions required for each menu
The implementation obtains the menu list according to the role, that is, which roles have permissions for each menu |
---|
- Modify the menu entity class and add role attributes
- service interface
- mapper
<!--Submenu, inherit above DdMenu Mapping object--> <resultMap id="MenusWithRoler" type="com.dd.security.entity.DdMenu" extends="BaseResultMap"> <collection property="roles" ofType="com.dd.security.entity.DdRole"> <id column="rid" property="id" /> <result column="rname" property="name" /> <result column="rnameZh" property="nameZH" /> </collection> </resultMap> <select id="getMenusWithRole" resultMap="MenusWithRoler"> select m.*, r.id as rid, r.`name` as rname, r.name_zh as rnameZh from dd_menu as m inner join dd_role_menu as rm on m.id = rm.mid inner join dd_role as r on r.id = rm.rid order by m.id </select>
2. The spring security filter intercepts the request, obtains the url, and determines which role permissions are required for the url
Forgot to add @ Component annotation in the screenshot
import com.dd.security.entity.DdMenu; import com.dd.security.entity.DdRole; import com.dd.security.service.DdMenuService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.util.AntPathMatcher; import java.util.Collection; import java.util.List; /** * Permission control * Analyze the required roles according to the request url */ @Component public class CustomFilter implements FilterInvocationSecurityMetadataSource { @Autowired private DdMenuService ddMenuService; private AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException { //Get FilterInvocation object FilterInvocation filterInvocation = (FilterInvocation) o; //Get request url String requestUrl = filterInvocation.getRequestUrl(); //Get the corresponding role of each menu, that is, the url. Which roles have permissions List<DdMenu> menusWithRole = ddMenuService.getMenusWithRole(); //Traverse menu for (DdMenu m : menusWithRole){ //If the user request url matches the url in the menu if(antPathMatcher.match(m.getUrl(),requestUrl)){ //Streaming programming + lambda expression: Class Name:: method name, method reference, DdRole::getName refers to the getName method that references the DdRole class //Class name:: new construction method reference, String []: new refers to the construction method referencing String [], and constructs an array //To obtain the role list, that is, the url requested by the user, you need to have the following roles String[] roles = m.getRoles().stream().map(DdRole::getName).toArray(String[]::new); //org.springframework.security.access.SecurityConfig; //Put our role in Security return SecurityConfig.createList(roles); } } //If the user request url is not the url in the menu, give the default role role_ Login (login role), that is, you must have a login role to access return SecurityConfig.createList("ROLE_LOGIN"); } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> aClass) { return false; } }
2. Obtain the user role and judge whether the user role permission can access the url
1. Get roles
- Entity class
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @ApiModel(value="DdUser object,use Spring Security The framework should inherit UserDetails Interface, implement the method, and change the return value to true", description="") public class DdUser implements UserDetails { private static final long serialVersionUID=1L; @ApiModelProperty(value = "id") @TableId(value = "id", type = IdType.ID_WORKER_STR) private Integer id; private String username; private String password; @ApiModelProperty(value = "User role") @TableField(exist = false) private List<DdRole> roles; /** * All permissions * @return */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = roles.stream() //Traverse each role into the authority character object specified by Security, // Like role_ Admin should be encapsulated as new SimpleGrantedAuthority("ROLE_admin") .map(role -> new SimpleGrantedAuthority(role.getName())) .collect(Collectors.toList());//Then return to list return authorities;//Return role permissions to } /** * Whether the account has expired * @return */ @Override public boolean isAccountNonExpired() { return true; } /** * Is the account locked * @return */ @Override public boolean isAccountNonLocked() { return true; } /** * Is the voucher (password) expired * @return */ @Override public boolean isCredentialsNonExpired() { return true; } /** * Enable * @return */ @Override public boolean isEnabled() { return true; } }
- service
- mapper
<!--/** * Obtain user owned roles according to user id */--> <select id="getRolesByUserId" resultType="com.dd.security.entity.DdRole" parameterType="java.lang.Integer"> select r.id as id, r.name as name, r.name_zh as nameZh from dd_role as r inner join dd_user_role as ur on r.id = ur.rid where ur.uid = #{userId} </select>
- Modify and obtain user information
- Modify custom UserDetailsService
/** * Configure UserDetailsService */ @Bean @Override public UserDetailsService userDetailsService() { return username->{ DdUser user = ddUserService.getLoginInfoByUsername(username); if(user == null){ throw new UsernameNotFoundException("Username or password incorrect "); } List<DdRole> rolesByUserId = ddRoleService.getRolesByUserId(user.getId()); user.setRoles(rolesByUserId); return user; }; }
2. Use the security filter to determine whether the user role can be accessed
Forgot to add @ Component annotation in the screenshot
import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; /** * Permission control * Judge user role */ import java.util.Collection; @Component public class CustomUrlDecisionManager implements AccessDecisionManager { /** * * @param authentication Current access user * @param o * @param collection Collection<ConfigAttribute> We configured it in the previous filtercustomefilter implements filterinvocationsecuritymetadatasource * @throws AccessDeniedException * @throws InsufficientAuthenticationException */ @Override public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException { //Traverse ConfigAttribute for(ConfigAttribute configAttribute : collection){ //Obtain the current access url and the required role permissions. These values are set in CustomFilter String needRole = configAttribute.getAttribute(); //Determine whether the url can be accessed by logging in, and then set it in CustomFilter if("ROLE_LOGIN".equals(needRole)){ //If the current user is anonymous (not logged in), throw an exception and let the user log in if(authentication instanceof AnonymousAuthenticationToken){ throw new AccessDeniedException("Not logged in, please log in!!!"); }else{ return; } } //If the current url is not logged in, you can access it //Judge whether there is a required role in the current user's GrantedAuthority. If so, release it //If the user does not have a corresponding role, it will not be released. At the same time: users who do not log in will not be released because they do not have a role Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); for(GrantedAuthority authority:authorities){ if(authority.getAuthority().equals(needRole)){ return; } } } throw new AccessDeniedException("Insufficient permissions, please contact the administrator!!!"); } @Override public boolean supports(ConfigAttribute configAttribute) { return false; } @Override public boolean supports(Class<?> aClass) { return false; } }
3. Configure spring security to make the filter effective
Note: these filters will not filter the paths released in the Security configuration class |
---|
- Introduce two filters
- Configure two filters for dynamic permissions
@Override protected void configure(HttpSecurity http) throws Exception { //csrg is not required to use JWT http.csrf().disable() //Using Token, session is not required .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and()//Next, configure authorization .authorizeRequests() //The following authorization is configured in the configure (Web Security Web) method, which is not required here // . antMatchers("/login","/logout").permitAll() / / allow access to / login, and / logout requests can pass without authentication .anyRequest().authenticated()//In addition to the above configuration, all the remaining requests are intercepted and can only be accessed after authentication .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {//Dynamic permission configuration @Override public <O extends FilterSecurityInterceptor> O postProcess(O o) { o.setAccessDecisionManager(customUrlDecisionManager);//Determine whether the user role can access the url Filter o.setSecurityMetadataSource(customFilter);//Analyze the required role filter based on the request url return o; } }) .and()//Next, configure the cache .headers() .cacheControl() ; //Add JWT login authorization filter interceptor http.addFilterBefore(jwtAuthencationTokenFilter(), UsernamePasswordAuthenticationFilter.class); //Add custom unauthorized and unregistered results. The front and back ends are separated, and the status code needs to be returned http.exceptionHandling() .accessDeniedHandler(restAccessDeniedHandler) .authenticationEntryPoint(restAuthorizationEntryPoint); }
4. Test
3, Implementation of permission group function
1. Role
The ROLE of spring security must have a ROLE_ Prefix, otherwise it will not be captured by spring security |
---|
Correct role name: ROLE_admin,ROLE_adsfasdf
Bad role name: r_admin,admin,ROLE_
- Therefore, when we add a ROLE, we should judge whether the user uses ROLE_ At the beginning, the addition logic is not added, but directly executed
- Controller
import com.dd.common_utils.Result; import com.dd.security.entity.DdRole; import com.dd.security.service.DdRoleService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/security/dd-role") public class DdRoleController { @Autowired private DdRoleService ddRoleService; @ApiOperation("Get all roles") @GetMapping("/") public Result getAllRoles(){ List<DdRole> list = ddRoleService.list(); if(list.isEmpty()){ return Result.error().message("No role information obtained!!!"); } return Result.ok().data("RoleAllList",list); } @ApiOperation("Add role") @PostMapping("/") public Result addRole(@RequestBody DdRole ddRole){ //If the ROLE name is not ROLE_ First, add if(!ddRole.getName().startsWith("ROLE_")){ ddRole.setName("ROLE_"+ddRole.getName()); } if(ddRoleService.save(ddRole)){ return Result.ok().message("Added successfully"); } return Result.error().message("Add failed"); } @ApiOperation("Delete role") @DeleteMapping("/role/{id}") public Result deleteRole(@PathVariable(value = "id",name = "id") Integer id){ if(ddRoleService.removeById(id)){ return Result.ok().message("Delete succeeded"); } return Result.error().message("Deletion failed"); } }
2. Menu
- controller
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.dd.common_utils.Result; import com.dd.security.entity.DdMenu; import com.dd.security.entity.DdRoleMenu; import com.dd.security.service.DdMenuService; import com.dd.security.service.DdRoleMenuService; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; /** * <p> * Front end controller * </p> * * @author testjava * @since 2021-12-14 */ @RestController @RequestMapping("/security/dd-menu") public class DdMenuController { @Autowired private DdMenuService ddMenuService; @Autowired private DdRoleMenuService ddRoleMenuService; @ApiOperation(value = "By user id Query menu list") @GetMapping("/menu") public List<DdMenu> getMenusByUserId(){ return ddMenuService.getMenusByUserId(); } @ApiOperation(value = "Query all menus") @GetMapping("/menus") public Result getMenus(){ List<DdMenu> list = ddMenuService.list(); if(list.isEmpty()){ return Result.error().message("No menu!!"); } return Result.ok().data("menuAllList",list); } @ApiOperation(value = "According to role id Query menu id") @GetMapping("/menuIdByRoleId/{rid}") public Result getMenuIdByRoleId(@PathVariable Integer rid){ List<Integer> mids = ddRoleMenuService.list(new QueryWrapper<DdRoleMenu>().eq("rid", rid)) .stream().map(DdRoleMenu::getMid).collect(Collectors.toList()); if(mids.isEmpty()){ return Result.error().message("Failed to get menu"); } return Result.ok().data("menuId",mids); } @ApiOperation(value = "According to role id Query menu") @GetMapping("/menuByRoleId/{rid}") public Result getMenuByRoleId(@PathVariable Integer rid){ List<DdMenu> list = ddMenuService.getMenuByRoleId(rid); if(list.isEmpty()){ return Result.error().message("No menu!!"); } return Result.ok().data("menuList",list); } @ApiOperation(value = "Update role menu") @PutMapping("/") public Result updateMenuRole(Integer rid,Integer[] mids){ return ddMenuService.updateMenuRole(rid,mids); } }
- service
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.dd.common_utils.Result; import com.dd.security.entity.DdMenu; import com.dd.security.entity.DdRoleMenu; import com.dd.security.entity.DdUser; import com.dd.security.mapper.DdMenuMapper; import com.dd.security.mapper.DdRoleMenuMapper; import com.dd.security.service.DdMenuService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.dd.security.service.DdRoleMenuService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.awt.*; import java.util.List; /** * <p> * Service implementation class * </p> * * @author testjava * @since 2021-12-14 */ @Service public class DdMenuServiceImpl extends ServiceImpl<DdMenuMapper, DdMenu> implements DdMenuService { @Autowired private DdMenuMapper ddMenuMapper; @Autowired private DdRoleMenuMapper ddRoleMenuMapper; @Autowired private RedisTemplate redisTemplate; /** * Get menu based on user id */ @Override public List<DdMenu> getMenusByUserId() { //Get UserDetails object from Security global context DdUser user = (DdUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); //Get redis object ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); //First query from redis List<DdMenu> menus = (List<DdMenu>) valueOperations.get("menu_" + user.getId()); //If not in redis, query the mysql database if(CollectionUtils.isEmpty(menus)){ menus = ddMenuMapper.getMenusByUserId(user.getId()); //Put the queried content in reids valueOperations.set("menu_" + user.getId(),menus); } //Return results return menus; } /** * Get menu list by role */ @Override public List<DdMenu> getMenusWithRole() { return ddMenuMapper.getMenusWithRole(); } @Override public List<DdMenu> getMenuByRoleId(Integer rid) { return ddMenuMapper.getMenuByRoleId(rid); } /** * Update role menu * Delete all menus of the current role before updating * @param rid Role id * @param mids Menu id to update */ @Override @Transactional//When you write updates yourself, you must add transactions public Result updateMenuRole(Integer rid, Integer[] mids) { //Delete it all first, or you'll have to judge it again and again. It's too expensive ddRoleMenuMapper.delete(new QueryWrapper<DdRoleMenu>().eq("rid", rid)); //If the menu id to be updated is empty, return directly if(null == mids||mids.length == 0){ return Result.ok().message("Menu updated successfully"); } //If the menu id is passed, it will be updated ddMenuMapper.insertRecord(rid,mids); return null; } }
- mapper
<!--Update role menu--> <insert id="insertRecord"> insert into dd_role_menu(rid,mid) values <foreach collection="mids" item="mid" separator=","> (#{rid},#{mid}) </foreach> </insert>
3. Users
- controller
import com.dd.common_utils.Result; import com.dd.security.entity.DdMenu; import com.dd.security.entity.DdUser; import com.dd.security.service.DdUserService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.parameters.P; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; import java.util.List; /** * <p> * Front end controller * </p> * * @author testjava * @since 2021-12-13 */ @RestController @Api("Do not carry when passing query criteria security Field, only carry DdUser Fields of the object itself, JSON") @RequestMapping("/security/dd-user-role") public class DdUserRoleController { @Autowired private DdUserService ddUserService; @Autowired private PasswordEncoder passwordEncoder; @ApiOperation("Get all operators and their roles and permissions,Do not carry when passing query criteria security Field, only carry DdUser Fields of the object itself, JSON") @PostMapping("/") public Result getAllUser(@RequestBody DdUser ddUser){ List<DdUser> list = ddUserService.getAllUser(ddUser); if(list.isEmpty()){ return Result.error().message("No user information obtained!!!"); } return Result.ok().data("allUserList",list); } @ApiOperation("Update user") @PutMapping("/") public Result updateUser(@RequestBody DdUser ddUser){ ddUser.setPassword(null);//Direct password change is not allowed if(ddUserService.updateById(ddUser)){ return Result.ok().message("Modify user successfully!!!"); } return Result.error().message("Modification failed!!!"); } @ApiOperation("Update user owned roles") @PutMapping("/updateUserRole") public Result updateUserRole(Integer uid,Integer[] rids){ return ddUserService.updateUserRole(uid,rids); } @ApiOperation("Change Password") @PutMapping("/updatePasswordById/{id}/{password}") public Result updatePassword(@PathVariable Integer id, @PathVariable String password){ String encode = passwordEncoder.encode(password); DdUser ddUser = new DdUser(); ddUser.setId(id); ddUser.setPassword(encode); if(ddUserService.updateById(ddUser)){ return Result.ok().message("Password changed successfully!!!"); } return Result.error().message("Failed to modify password!!!"); } @ApiOperation("delete user") @DeleteMapping("/{id}") public Result deleteById(@PathVariable Integer id){ if(ddUserService.removeById(id)){ return Result.ok().message("Deleted successfully!!!"); } return Result.error().message("Delete failed!!!"); } }
- service
@Autowired private DdUserRoleMapper ddUserRoleMapper; /** * Get all operators */ @Override public List<DdUser> getAllUser(DdUser ddUser) { return ddUserMapper.getAllUser(ddUser); } /** * Update user owned roles * @param uid User id * @param rids Role id to be modified by the user */ @Override @Transactional//When you write updates yourself, you must add transactions public Result updateUserRole(Integer uid, Integer[] rids) { //Delete it all first, or you'll have to judge it again and again. It's too expensive ddUserRoleMapper.delete(new QueryWrapper<DdUserRole>().eq("uid", uid)); //If the menu id to be updated is empty, return directly if(null == rids||rids.length == 0){ return Result.ok().message("User role updated successfully"); } //If the menu id is passed, it will be updated ddUserMapper.insertRecord(uid,rids); return Result.ok().message("Menu updated successfully"); }
- mapper
<?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.dd.security.mapper.DdUserMapper"> <!--Result set encapsulation--> <resultMap type="com.dd.security.entity.DdUser" id="DdUserResult"> <result property="id" column="id" /> <result property="username" column="username" /> <result property="password" column="password" /> </resultMap> <resultMap id="DdUserRoleResult" type="com.dd.security.entity.DdUser" extends="DdUserResult"> <collection property="roles" ofType="com.dd.security.entity.DdRole"> <id column="rid" property="id" /> <result column="rname" property="name" /> <result column="rnameZh" property="nameZh" /> </collection> </resultMap> <select id="selectByUsername" parameterType="java.lang.String" resultType="com.dd.security.entity.DdUser"> select id,username,password from dd_user where username=#{username} </select> <select id="getAllUser" resultMap="DdUserRoleResult" parameterType="com.dd.security.entity.DdUser"> select u.id as id, u.username as username, u.`password` as `password`, r.id as rid, r.`name` as rname, r.name_zh as rnameZh from dd_user as u inner join dd_user_role as ur on u.id = ur.uid inner join dd_role as r on ur.rid = r.id <where> <trim> <if test="id != null and id != '' and id!=0">and u.id = #{id}</if> <if test="username != null and username != ''">and u.username like concat('%', #{username}, '%')</if> </trim> </where> </select> <!--Update user roles--> <insert id="insertRecord"> insert into dd_user_role(uid,rid) values <foreach collection="rids" item="rid" separator=","> (#{uid},#{rid}) </foreach> </insert> </mapper>
4. Main function test
- menu
4, Front end docking
Due to space constraints, I put it in this article |
---|