编程知识 cdmana.com

Introduction to spring security (3)

I don't say much nonsense , Just start this SpringSecurity The learning program of .

Database description

User information sheet USER Store user information

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
    `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT 'id',
    `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT ' name ',
    `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT ' password ',
    `isEnable` bit(1) NULL DEFAULT NULL COMMENT ' Is it enabled? 1、 Enable  0、 Ban ',
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

Permissions on the table PURVIEW Save permission information

DROP TABLE IF EXISTS `purview`;
CREATE TABLE `purview`  (
    `id` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT ' jurisdiction id',
    `authority` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT ' Permission to name ',
    `role` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT ' role ',
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

User authority table PURVIEW Save user permissions

DROP TABLE IF EXISTS `authority`;
CREATE TABLE `authority`  (
    `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT ' User permissions id',
    `authority` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT ' jurisdiction id',
    `member_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT ' user id',
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

The basis of Security Security configuration

  1. Configure permissions to SpringSecurity
  2. Configure whether to intercept requests
  3. Configure request processing (success/error)

How to configure ?

SecurityVerificationConfiguration Configuration class is also the core class , Some information about the above is configured

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityVerificationConfiguration extends WebSecurityConfigurerAdapter {

    /**
     *  Password encryption 
     *
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     *  Interceptor 
     */
    @Autowired
    public JwtAuthenticationFilter jwtAuthenticationFilter;

    /**
     * jwt  Verify the processor 
     */
    @Autowired
    public JwtAccessDeniedHandler jwtAccessDeniedHandler;

    /**
     * toekn  To configure 
     */
    @Autowired
    public TokenConfiguration tokenConfiguration;

    @Autowired
    PurviewService purviewService;
    
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     *  to grant authorization  、  verification 
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //  Add permissions 
        selectPurview(http);

        http
                .authorizeRequests()
                //  The authorized address does not need to be verified 
                .antMatchers("/auth/token").permitAll()
                //  User registration address 
                .antMatchers("/user/registered").permitAll()
                //  The rest need to be verified 
                .anyRequest().authenticated()
                .and()
                //  Add post-processing interceptor 
                .addFilterAfter(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                //  Access denial handler 
                .accessDeniedHandler(jwtAccessDeniedHandler)
                .and()
                .apply(tokenConfiguration)
                .and()
                //  Cancel  session  The state of 
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf().disable();
    }

    /**
     *  Query permissions and put permissions into  security  in 
     *
     * @param http
     * @throws Exception
     */
    public void selectPurview(HttpSecurity http) throws Exception {
        List<Purview> purviews = purviewService.selectList();
        for (Purview purview : purviews) {
            http.authorizeRequests()
                    .antMatchers(purview.getAuthority()).hasAnyAuthority(purview.getRole());
        }
    }

}

About SecurityVerificationConfiguration Explain that I hope to help you solve some doubts

  • @EnableWebSecurity Annotations to open web Security verification
  • @EnableGlobalMethodSecurity(prePostEnabled=true) Enable annotation based security Refer to official documentation 11.5 It can be opened through @PreAuthorize Annotation limit request controller Access rights of
  • selectPurview(HttpSecurity http) Method to query all permission roles , And assign the permission role to SpringSecurity management
  • PasswordEncoder Password encryption Refer to official documentation 5.1.2
  • JwtAuthenticationFilter yes TOEKM Interceptor

Looked at the basic configuration , according to SpringSecurity introduction ( Two ) The ideas mentioned in , Need to build Authentication Certification object , So here's what you need to build Authentication Authentication Certification object

How to build ?

In the build Authentication Before authenticating the object , There is also a problem that needs to be clarified , structure Authentication The information required by the authentication object :

  1. principal Obviously, this uses final Modification cannot be modified , Therefore, the value passed must not need to be modified after authentication , for example : User information
  2. credentials Information used to prevent authentication , It can be TOKEN
  3. authorities Privilege set

principal It's easy to build a user , like this

public User(String username, String password, Collection<? extends GrantedAuthority> authorities) 

credentials It's even simpler , hold jwt Generated TOKEN Just put it in

authorities Privilege set , It's not difficult. , Just look up the permissions that the user has , There are two ways , One is to obtain the user's permission through user information , Another is through TOKEN Get permission information , But the first is more restrictive , The second is also more convenient , All we do is parse TOKEN To get the user's permission

How to build this first TOKEN It's the first thing

Build build build TOKEN
@Component
@Slf4j
public class JwtUtil {

    /**
     *  Signature key 
     */
    @Value("${auth.token.signingKey}")
    private String signingKey;

    /**
     *  Create build  token
     * <p>
     * setClaims()  And  setSubject()  Conflict, so the subject information is not set 
     *
     * @param claim  User permissions  map
     * @return String  Generated  token
     */
    public String createToken(Map<String, Object> claim) {
        return Jwts.builder()
                //  Set unique  ida
                .setId(IdUtil.simpleUUID())
//                .claim("auth", "admin")
                .setClaims(claim)
                //  Set expiration time 
                .setExpiration(new DateUtil().getNowDateOneTime())
                //  Set up  token  Time of issue 
                .setIssuedAt(new DateTime())
                //  Set signature   Use HS256 Algorithm , And set up SecretKey( character string )   Signature algorithm and secret key 
                .signWith(SignatureAlgorithm.HS256, signingKey)
                //  The following constructs JWT And serialize it into compact ,URL Safe string 
                .compact();
    }
    
}    

Here we need to pay attention to distinguish claim(String , Object) and setClaims(Map<String, Object>)

  • claim Only a single permission can be set
  • setClaims You can set multiple permission objects

Here we suggest the second , So you can get from TOKEN Store more valid information in

analysis TOKEN structure Authentication Certification object
@Slf4j
@Component
public class TokenProvider {

    //  Permission key 
    private static final String AUTHORITIES_KEY = "auth";

    //  User information 
    private static final String ID = "id";

    //  Signature key 
    @Value("${auth.token.signingKey}")
    private String signingKey;

    @Autowired
    JwtUtil jwtUtil;

    /**
     *  obtain  Spring Context  Of  SecurityContext  object 
     *  Used to obtain user authentication 
     *
     * @param token jwt  Generated  token  Information 
     * @return authentication  Certification object 
     */
    public Authentication getAuthentication(String token) {

        // parser()  analysis token
        Claims claims = Jwts.parser()
                .setSigningKey(signingKey)
                .parseClaimsJws(token)
                .getBody();
        Object claim = claims.get(AUTHORITIES_KEY);

        //  jurisdiction 
        String auth = "";
        if (Objects.nonNull(claim)) {
            auth = claim.toString();
        }

        //  Privilege set 
        Collection<? extends GrantedAuthority> authorities =
                Arrays.stream(auth.split(","))
                        .filter(StringUtils::isNotBlank)
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());

        //  establish  Spring Security  Of  user  object 
        User principal = new User((String) claims.get(ID), "", authorities);

        //  Create return  Authentication  object 
        return new UsernamePasswordAuthenticationToken(principal, token, authorities);
    }

}

You need to start with TOKEN In order to get Claims object , And get permission information , Build first principal User information , Then build a... Through user information Authentication Certification object , It's almost finished here 80% 了 , The code is completed according to the previous idea , But there are several issues to consider

  1. When to build TOKEN Information Of course, it is to build such a token of security authentication information when the user logs in , And you need to carry the token when accessing
  2. When to parse TOKEN Information , structure Authentication Certification object Of course, every time you access the interface

jurisdiction 、 User information , Give it all to me now Spring Security Management of the , But how to build this every time you access the interface Authentication What about the authentication object ?

As a reminder , Interceptor ,OncePerRequestFilter You can ensure that a request will only pass the filter once

Filter and intercept requests
@Component
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    /**
     *  User's business logic layer 
     */
    @Autowired
    public UserService userService;

    @Autowired
    public JwtUtil jwtUtil;

    @Autowired
    private TokenProvider tokenProvider;

    public JwtAuthenticationFilter(TokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    /**
     *  And {@code doFilter} The contract is the same , But ensure that each request is called only once in a single request thread .
     *  For more information , Please see the {@link #shouldNotFilterAsyncDispatch()}.
     * <p> Provide HttpServletRequest and HttpServletResponse Parameters , Not the default ServletRequest and ServletResponse Parameters .
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws IOException, ServletException {
         if (!"/auth/token".equals(httpServletRequest.getRequestURI())) {
            String token = httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION);
            if (token == null)
                throw new TokenException(HttpStatus.HTTP_FORBIDDEN, " Missing validation information ");

            Authentication authentication = tokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

}

Some interfaces need to be excluded , For example, login authentication interface , The rest need to build such Authentication Certification object

This is a set of... Implemented according to our ideas authentication , The code of the specific operation table is not shown , Just follow the train of thought and go down .

Shao Jie : There may be some loopholes in the code 、BUG And so on , Logic may not be perfect , Just provide some ideas , For reference only , What exactly needs to be done , Refer to official documents by yourself , The official has a more detailed explanation , Just pure hope , Can help you , Code problems can be found in GitHub carry ISSUES

Participation of this paper Tencent cloud media sharing plan , You are welcome to join us , share .

版权声明
本文为[Shao Jie]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/08/20210809183600807L.html

Scroll to Top