Authentication and authorization Spring Security

How to introduce Spring Security as the authority authentication service of the project

1. Introduce dependencies

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

2. Configure 4 class
content resources

3. Simple test

@Slf4j
@RestController
public class LoginController {
    
    

    @Autowired
    XcUserMapper userMapper;


    @RequestMapping("/login-success")
    public String loginSuccess() {
    
    

        return "登录成功";
    }


    @RequestMapping("/user/{id}")
    public XcUser getuser(@PathVariable("id") String id) {
    
    
        XcUser xcUser = userMapper.selectById(id);
        return xcUser;
    }
    }

What is OAuth2?

OAuth2 is an open network standard about authorization, which is widely used all over the world. The current version is version 2.0.
1 It provides a secure, open and proposed standard for authorization of user resources. The function of OAuth2 is to solve the security and flexibility of accessing resources.
2. It defines a mechanism for obtaining requested resources through an access token, but does not define a standard method for providing user identity information.
3. OAuth2 can be used to implement secure delegated access, based on HTTPS, and APIs, Service applications use access tokens for authentication

There are four modes of OAuth2 , namely authorization code mode, simplified mode, password mode and client mode. The most commonly used ones are authorization code mode and password mode.
A typical example of authorization code mode is WeChat scan code authentication. You can click on the link to learn more about the specific implementation.

How to unify the authentication entry in Spring Security?

(Just like you can log in to the game with multiple login methods, such as scan code login, account password login)
1. Create a new object class, no matter what type of login front-end parameters must be consistent
2. Create a new implementation class to implement UserDetailsService, Rewrite the loadUserByUsername method, which is the unified entrance of the entire program. Once you click to log in, this method will be called first, and you can write logic in this method
insert image description here

/**
 * @author Mr.M
 * @version 1.0
 * @description 认证用户请求参数
 * @date 2022/9/29 10:56
 */
@Data
public class AuthParamsDto {
    
    

    private String username; //用户名
    private String password; //域  用于扩展
    private String cellphone;//手机号
    private String checkcode;//验证码
    private String checkcodekey;//验证码key
    private String authType; // 认证的类型   password:用户名密码模式类型    sms:短信模式类型
    private Map<String, Object> payload = new HashMap<>();//附加数据,作为扩展,不同认证类型可拥有不同的附加数据。如认证类型为短信时包含smsKey : sms:3d21042d054548b08477142bbca95cfa; 所有情况下都包含clientId


}
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.ucenter.mapper.XcUserMapper;
import com.xuecheng.ucenter.model.dto.AuthParamsDto;
import com.xuecheng.ucenter.model.dto.XcUserExt;
import com.xuecheng.ucenter.model.po.XcUser;
import com.xuecheng.ucenter.service.AuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.List;

//统一认证入口,账号密码、微信扫码登录
@Slf4j
@Component
public class UserServiceImpl implements UserDetailsService {
    
    
    @Autowired
    XcUserMapper xcUserMapper;

    @Autowired
    ApplicationContext applicationContext;

//    以后传入的对象就是AuthParamsDto,根据参数的不同来识别用户的登录类型
    
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
    
    
        //1.统一的请求参数,获取并转成实体类
        AuthParamsDto authParamsDto =null;
        try {
    
    
             authParamsDto = JSON.parseObject(s, AuthParamsDto.class);
        } catch (Exception e) {
    
    
            throw new RuntimeException("请求认证不符合参数要求");
        }
        //2.wx,password,third,根据不同的登录类型走不同的实现类
        String authType = authParamsDto.getAuthType();
        //3.不同的登录方法对数据库操作的逻辑不一样,如微信扫码(获取第三方access token就可以获取信息保存到数据库)和账号密码登录(验证账号密码的正确性,然后查库获取信息)验证的逻辑不一样
        String beanName=authType+"_authService";
        AuthService authService = applicationContext.getBean(beanName, AuthService.class);
        XcUserExt xcUserExt = authService.execute(authParamsDto);
        
        //4.根据类型执行验证逻辑,给前端封装令牌
        UserDetails userDetails = getUserPrincipal(xcUserExt);

        return userDetails;
    }

    private UserDetails getUserPrincipal(XcUserExt xcUser) {
    
    
        String password = xcUser.getPassword();
        xcUser.setPassword(null);
        //把用户信息转成json放入令牌中,方便其它服务拿到
        String userJson = JSON.toJSONString(xcUser);
        // 根据用户id查询全息
        String [] authorities={
    
    "test"};
        List<String> list=xcUserMapper.selectAuthoritiesByUserId(xcUser.getId());
        if (list.size()>0){
    
    
            authorities= list.toArray(new String[list.size()]);
        }
        //权限
        UserDetails userDetails = User.withUsername(userJson).password(password).authorities(authorities).build();
        return userDetails;
    }
}

For the third step, the design is that different implementation classes of an interface follow different logics according to the value of @Service("xxx_authService")
account password login logic

package com.xuecheng.ucenter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.ucenter.feignclient.CheckCodeClient;
import com.xuecheng.ucenter.mapper.XcUserMapper;
import com.xuecheng.ucenter.model.dto.AuthParamsDto;
import com.xuecheng.ucenter.model.dto.XcUserExt;
import com.xuecheng.ucenter.model.po.XcUser;
import com.xuecheng.ucenter.service.AuthService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

//账号密码登录
@Service("password_authService")
public class PasswordAuthServiceImpl implements AuthService {
    
    
    @Autowired
    XcUserMapper xcUserMapper;

    //框架自带
    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    CheckCodeClient checkCodeClient;
    @Override
    public XcUserExt execute(AuthParamsDto authParamsDto) {
    
    
        //账号
        String username = authParamsDto.getUsername();
        //验证码 todo
        //前端输入的验证码
        String checkcode = authParamsDto.getCheckcode();
        //验证码存储的key
        String checkcodekey = authParamsDto.getCheckcodekey();
        if (StringUtils.isEmpty(checkcode)||StringUtils.isEmpty(checkcodekey)){
    
    
            throw new RuntimeException("请输入验证码");
        }

        Boolean verify = checkCodeClient.verify(checkcodekey, checkcode);
        if (verify==null||!verify){
    
    
            throw new RuntimeException("验证码输入错误");
        }

        //1.s就是username,根据username查询数据库,查询用户是否存在
        XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, username));
        //2.查询为空报错
        if (xcUser==null){
    
    
            throw new RuntimeException("账号不存在");
        }
        //3.数据库密码
        String password = xcUser.getPassword();
        //校验
        String inputPassword = authParamsDto.getPassword();
        boolean matches = passwordEncoder.matches(inputPassword, password);
        if (!matches){
    
    
            throw new RuntimeException("账号或密码错误");
        }
        XcUserExt xcUserExt = new XcUserExt();
        BeanUtils.copyProperties(xcUser, xcUserExt);

        return xcUserExt;
    }
}

The logic of WeChat login (another implementation class)

    @Override
    public XcUserExt execute(AuthParamsDto authParamsDto) {
    
    
        //得到账号
        String username = authParamsDto.getUsername();
        //查询数据库
        XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, username));
        if(xcUser == null){
    
    
            throw new RuntimeException("用户不存在");
        }

        XcUserExt xcUserExt = new XcUserExt();
        BeanUtils.copyProperties(xcUser, xcUserExt);

        return xcUserExt;
    }

How to provide resource permission verification based on the functions that come with the Spring Security framework?

For example, ordinary employees cannot call to view employee salaries, but managers can authorize RBAC.
1. Preliminary conditions, SpringSecurity is introduced and the service of the interface to be controlled by permissions is configured with SpringSecurity configuration
data table
insert image description here

2. Use @PreAuthorize("hasAnyAuthority('xc_teachmanager_course_list')") in the controller class
//Specify the authority identifier
as long as it has the authority in the token it carries.
On the premise that there is already a SpringSecurity microservice, others If the interface of microservices wants to restrict permissions, it is necessary to integrate OAuth2 in these microservices. This way, when users access these microservices, they can be authenticated and authorized via OAuth2.
import configuration

package com.xuecheng.content.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.util.Arrays;

/**
 * @author Administrator
 * @version 1.0
 **/
@Configuration
public class TokenConfig {
    
    

    String SIGNING_KEY = "mq123";


//    @Bean
//    public TokenStore tokenStore() {
    
    
//        //使用内存存储令牌(普通令牌)
//        return new InMemoryTokenStore();
//    }

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Bean
    public TokenStore tokenStore() {
    
    
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
    
    
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }


}

Note: Permissions are set in the implementation class of UserDetailsService. This is in the SpringSecurity service. Other microservices do not need to be modified, just indicate here
insert image description here

Also query the user's permissions in the database

Guess you like

Origin blog.csdn.net/qq_56533553/article/details/130135529