Series directory
SpringSecurity rights management system actual combat – one, project introduction and development environment preparation
SpringSecurity rights management system practice – two, log, interface documents and other implementation
SpringSecurity rights management system practice – three, the main page and interface implementation
SpringSecurity rights management system actual combat – four, integration of SpringSecurity (part 1)
SpringSecurity rights management system practice – five, integration of SpringSecurity (under)
SpringSecurity authority management system combat – six, SpringSecurity integration JWT
SpringSecurity rights management system practice – seven, deal with some problems
SpringSecurity rights management system practice – eight, AOP record user logs, abnormal logs
SpringSecurity rights management system actual combat – nine, data rights configuration
preface
In the previous article, SpringSecurity integrated half of it, and this time completes the other half, so the serial number of this article continues.
Customize user information
Previously we logged in using either the specified username and password or springsecurity’s default username and printed password. To connect to the custom database, we just need to implement a custom UserDetailsService.
Let’s create a new JwtUserDto that inherits UserDetails and implements its methods
@Data
@AllArgsConstructor
public class JwtUserDto implements UserDetails {
// User data
private MyUser myUser;
// Set of user permissions
@JsonIgnore
private List<GrantedAuthority> authorities;
public List<String> getRoles(a) {
return authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
}
// Encrypted password
@Override
public String getPassword(a) {
return myUser.getPassword();
}
/ / user name
@Override
public String getUsername(a) {
return myUser.getUserName();
}
// Whether to expire
@Override
public boolean isAccountNonExpired(a) {
return true;
}
// Whether to lock
@Override
public boolean isAccountNonLocked(a) {
return true;
}
// Whether the credentials are expired
@Override
public boolean isCredentialsNonExpired(a) {
return true;
}
// Whether it is available
@Override
public boolean isEnabled(a) {
return myUser.getStatus() == 1 ? true : false; }}Copy the code
Define a user-defined UserDetailsServiceImpl implementation UserDetailsService
@Service
@Slf4j
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Autowired
private MenuDao menuDao;
@Override
public JwtUserDto loadUserByUsername(String userName) throws UsernameNotFoundException {
MyUser user = userService.getUser(userName);// Get the user based on the user name
if (user == null) {throw new UsernameNotFoundException("User name does not exist");// This exception must be thrown
}else if (user.getStatus().equals(MyUser.Status.LOCKED)) {
throw new LockedException("User is locked. Please contact administrator.");
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
List<MenuIndexDto> list = menuDao.listByUserId(user.getId());
List<String> collect = list.stream().map(MenuIndexDto::getPermission).collect(Collectors.toList());
for (String authority : collect){
if(! ("").equals(authority) & authority ! =null){
GrantedAuthority grantedAuthority = newSimpleGrantedAuthority(authority); grantedAuthorities.add(grantedAuthority); }}// Add user permissions to the GrantedAuthority collection
JwtUserDto loginUser =new JwtUserDto(user,grantedAuthorities);
returnloginUser; }}Copy the code
Mybatis data is null, if you never modify the data, it is null. If you modify it and then delete it, it will be null.
The listByUserId method in meudao
@Select("SELECT DISTINCT sp.id,sp.parent_id,sp.name,sp.icon,sp.url,sp.type,sp.permission " + "FROM my_role_user sru " + "INNER JOIN my_role_menu srp ON srp.role_id = sru.role_id " + "LEFT JOIN my_menu sp ON srp.menu_id = sp.id " + "WHERE " + "sru.user_id = #{userId}")
@Result(property = "title",column = "name")
@Result(property = "href",column = "url")
List<MenuIndexDto> listByUserId(@Param("userId")Integer userId);
Copy the code
Eight, encryption,
Let’s talk about the importance of encryption.
In 2011, a Chinese developer community (CSDN?) was attacked database, more than 6 million user accounts stored in plaintext were disclosed, and a large number of user privacy was leaked.
This is an old meme. In almost every blog post about the importance of encryption, the CSDN issue is brought up.
So why is password encryption important? If your password is also in the hands of a hacker when your database is compromised, then even if you fix the problem, the hacker still has the user’s password.
So we need to avoid this problem as much as possible at the beginning of the system development.
So with all that said, how do you encrypt?
In fact, SpringSecurity has built-in password encryption mechanism, just need to implement a PasswordEncoder interface.
Take a look at the source code
public interface PasswordEncoder {
String encode(CharSequence var1);
boolean matches(CharSequence var1, String var2);
default boolean upgradeEncoding(String encodedPassword) {
return false; }}Copy the code
- Encode (): Parses parameters according to specific parsing rules.
- Matches () verifies that the encoded password retrieved from the store matches the original password submitted after encoding. Return true if the passwords match; If there is no match, return false.
- UpgradeEncoding () : Returns true if the parsed password can be parsed again with a more secure result, false otherwise. False is returned by default.
The first parameter indicates the password that needs to be resolved. The second parameter indicates the stored password.
Spring Security also has several commonly used PasswordEncoder interfaces built in, with BCryptPasswordEncoder being the official recommendation
. Let’s configure it. Add the following code in the SpringConfig category.
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder(a){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}// Customize the userDetailsService encryption
Copy the code
The console will no longer print the password. Now you need to enter the username and password in the database to log in.
Access to user information
When we drew the menu, we wrote out the user ID. Now we’re going to get the user information from SpringSecurity.
There are two ways to retrieve logged-in user information, one from the session, and the other provided by SpringSecurity. Choose the latter method here.
We can obtain the information of the user after login through the following methods (there are other methods such as obtaining the login IP, which will not be described in detail).
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
Copy the code
Let’s convert the type
JwtUserDto jwtUserDto = (JwtUserDto)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Copy the code
Print jwtUserDto and see that we actually got the user’s information
So let’s rewrite the method of getting the menu by user ID
@GetMapping(value = "/index")
@ResponseBody
@apiOperation (value = "get menu by user ID ")
public List<MenuIndexDto> getMenu(a) {
JwtUserDto jwtUserDto = (JwtUserDto)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Integer userId = jwtUserDto.getMyUser().getId();
return menuService.getMenu(userId);
}
Copy the code
Delete the userId before writing the front end dead. Now we can automatically draw menus based on the login user.
The user has the admin permission
A user with common rights
Ten, authorization,
At present, we only draw the interface that users with different permissions can operate, but there is no real permission control.
Previously in 7, we put the set of permissions owned by each user into the GrantedAuthority collection
In the user information printed previously, you can see that the user has the authorities
SpringSecurity automatically does the permissions for us. All we need to do is add permission flags to the methods that need permission control.
For example, the permission id of a user is user:list
We simply add @preauthorize (“hasAnyAuthority(‘user:list’)”) to the related interfaces
@GetMapping("/index")
@PreAuthorize("hasAnyAuthority('user:list')")
public String index(a){
return "system/user/user";
}
@GetMapping
@ResponseBody
@apiOperation (value = "user list ")
@PreAuthorize("hasAnyAuthority('user:list')")
public Result<MyUser> userList(PageTableRequest pageTableRequest, UserQueryDto userQueryDto){
pageTableRequest.countOffset();
return userService.getAllUsersByPage(pageTableRequest.getOffset(),pageTableRequest.getLimit(),userQueryDto);
}
Copy the code
Now we log in ordinary user to operate the related interface, found an error
Console printing
Next, we need to modify all interfaces, add corresponding annotations on the interfaces that need permission control, please do it yourself.
Custom exception handling
Although now the function has been implemented, although the user cannot access the function without permission, but the exception is not handled. If you click, the user will see a string of error messages if the front end does not intercept incorrectly, which is very unfriendly and can cause stress to the server.
We just need to catch the exception shown above in the global exception handling class we created earlier.
@ExceptionHandler(AccessDeniedException.class)
public Result handleAuthorizationException(AccessDeniedException e)
{
log.error(e.getMessage());
return Result.error().code(ResultCode.FORBIDDEN).message("No permission, please contact the administrator for authorization.");
}
Copy the code
Restarting the project and writing the rules on the front end will be user friendly
12. Custom log out
By default, SpringSecurity registers a /logout route that can be used to logout login states, including Session and remember-me.
We can define the rule directly in Configure of SpringSecurityConfig, similar to formLogin. You can also customize a LogoutHadnler (see this article)
Now that some of SpringSecurity’s common features are in place, we’ll integrate JWT for stateless login in the next section
This seriesgiteeandgithubSynchronous update in