Spring Security

------------------------------------

学习Springboot的时候因为要加上一些权限控制等操作,所以捣鼓了一下SpringBoot Security。记录一下。

------------------------------------

涉及的相关类:

WebSecurityConfig:Spring Security配置类。

MyPasswordEncoder:Spring security 5.0中新增了多种加密方式,也改变了密码的格式,为了更安全,具体可以在网上搜索这个错误报错进行详细了解:There is no PasswordEncoder mapped for the id “null”。在此处我是自己定义了这个密码验证方式替换掉了security的原先密码验证,因为暂时我这边用百度的答案解决不了上面我说的这个报错。

AuthSuccessHandler:登录成功后的处理类。

UserDetailsImpl:此类实现 UserDetails接口,用于进行用户验证,相当于security体系里的用户类。

UserService:此类实现 UserDetailsService,当执行登录操作之后,security会利用接口的 loadUserByUsername方法根据传进来的用户名查找对应的用户信息,然后组装成一个新的 UserDetailsImpl 提交给security 进行信息验证。

UserUtil:自定义的用户工具类,目前用于获取当前用户和获取用户是否有指定权限。

User:自定义用户类,是数据库用户表的实体类,其实UserDetailsImpl 可以继承此类更加方便,不过此处我分开来写了。

Role:自定义角色类,是数据库角色表的实体类。

UserRole:自定义用户角色关联类,是数据库用户角色关联表的实体类。

Func:自定义相关功能权限的类,是数据库功能权限表的实体类。

RoleFunc:自定义角色权限关联类,是数据库角色权限关联的实体类。

此外还有以上相关实体类的5个 Repository 接口以及部分Service类。

------------------------------------

1.首先配置一个config类继承 WebSecurityConfigurerAdapter,重写里面的几个方法以配置登录登出和权限控制。

package com.didispace;

import com.didispace.security.AuthSuccessHandler;
import com.didispace.security.MyPasswordEncoder;
import com.didispace.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public static MyPasswordEncoder passwordEncoder() {
        return new MyPasswordEncoder();
    }
    @Autowired
    private AuthSuccessHandler authSuccessHandler;
    @Autowired
    private UserService userService;

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
        // 配置访问那些资源不用进行验证,在此处配置是会直接绕开spring security的所有filter,直接跳过验证
        web.ignoring().antMatchers("/images/**","/js/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.csrf().disable(); // 一般来说security自动开启防御csrf,类似那种ajax post请求就访问不了,可以调用disable方法注销调,当然这不安全。
        http
            .authorizeRequests()
                // http.permitAll不会绕开springsecurity验证,相当于是允许该路径通过
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                // 配置登录页面的访问路径
                .loginPage("/login")
                // 登录路径允许不用验证即可访问
                .permitAll()
                // 配置登录验证成功后的处理类/方法
                .successHandler(authSuccessHandler)
                .and()
            // Spring Security已经有登出的方法,这里配置登出也是可以直接访问
            .logout()
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//        auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
        // 这里配置security校验的方式和校验需要的一些组件
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

}

2.AuthSuccessHandler是我自定义的security验证成功后的处理类,负责登录成功后重定向到指定的页面以及加载角色权限,将权限相关的信息保存到security控制的用户类里面。方法的参数 authentication 就是security用于维护用户验证信息的类,里面维护着上述的UserDetailsImpl对象。

package com.didispace.security;

import com.didispace.domain.UserDetailsImpl;
import com.didispace.service.FuncService;
import com.didispace.util.UserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 *  登录验证成功后的处理类
 */
@Component
public class AuthSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private FuncService funcService;
    @Autowired
    private UserUtil userUtil;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        Object principal = authentication.getPrincipal();
        if (principal instanceof UserDetails) {
            System.out.println(" success !! ");
            UserDetailsImpl userDetails = (UserDetailsImpl) principal;
            // 从数据库找到该用户相关的所有权限并存到 security 维护的用户类里面
            userDetails.setFunctionCodes(funcService.findFunctionCodesByRoleIds(userDetails.getUsername()));
        }
        // 将用于获取用户权限的方法/类存储到session里面,方便 html 调用方法来判断当前用户是否有权限进行操作
        httpServletRequest.getSession().setAttribute("userUtil",userUtil);
        // 重定向到指定的页面
        httpServletResponse.sendRedirect("hello");
    }

}

3.UserService实现 UserDetailsService 接口,要实现其中的 loadUserByUsername方法,如同我在上面简介说的功用。然后在config类的security验证方式配置中加入此实现类的对象。

package com.didispace.service;

import com.didispace.domain.User;
import com.didispace.domain.UserDetailsImpl;
import com.didispace.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
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.Service;

import java.util.ArrayList;

@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userRepository.findByUserName(s);
        if (user == null) {
            throw new UsernameNotFoundException("没有此用户");
        }
        System.out.println("user_name : " + user.getUserName());
        System.out.println("pass_word : " + user.getPassWord());
        // 创建UserDetails的实现类,第三个参数未空列表在UserDetailsImpl里面可以看到
        return new UserDetailsImpl(user.getUserName(),user.getPassWord(), new ArrayList<>());
    }

}

4.再来看看security维护的用户类 UserDetailsImpl ,实现 UserDetails接口,普遍做法是此类也继承 User实体类。实现UserDetails,会要求实现其中的几个方法,比如 getPassword 和 getUsername,在实现的时候返回类中的关于用户名和密码的字段即可,hasFunctionCode是给 UserUtil 的 hasFunction方法调用的,用以html页面判断是否有相关权限,权限资料在调用AuthSuccessHandler的方法的时候就set进去了,下图还有几个需要实现的方法用默认实现或者网上查查即可。

package com.didispace.domain;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class UserDetailsImpl implements UserDetails {

    private String userName;

    private String passWord;

    private List<String> functionCodes;

    public List<String> getFunctionCodes() {
        return functionCodes;
    }

    public void setFunctionCodes(List<String> functionCodes) {
        this.functionCodes = functionCodes;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String functionCode : functionCodes) {
            authorities.add(new SimpleGrantedAuthority(functionCode));
        }
        return authorities;
    }

    public UserDetailsImpl(String userName, String passWord, List<String> functionCodes) {
        this.userName = userName;
        this.passWord = passWord;
        this.functionCodes = functionCodes;
    }

    public boolean hasFunctionCode(String... funcCode) {
        for(String code :funcCode){
            for(String func:getFunctionCodes()){
                if(func != null && func.toString().equals(code)){
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public String getPassword() {
        return this.passWord;
    }

    @Override
    public String getUsername() {
        return this.userName;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

此外我们还可以看到有个 getAuthorities的实现方法,这个是security默认机制处理权限用的,在上面我们可以看到我们自己自定义一些方法例如UserUtil 的来获取权限信息,但其实也可以在UserUtil 中执行getAuthorities方法获取权限信息,同理只要在AuthSuccessHandler的方法的时候就set进去相关资料就行。关于这个security方法或security的权限控制的详细信息推荐大家看一下地址:

https://www.cnblogs.com/longfurcat/p/9417422.html

 

5.接下来看看UserUtil,hasFunction如上所说给前端页面调用,此类在AuthSuccessHandler的方法的时候就set进去,getCurrUser方法可以获取当前用户:

import com.didispace.domain.UserDetailsImpl;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

@Component
public class UserUtil {

    public static UserDetailsImpl getCurrUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return null;
        }
        if (authentication instanceof AnonymousAuthenticationToken) {
            return null;
        }
        UserDetailsImpl user =
                (UserDetailsImpl) (authentication.getPrincipal() == null ? null : authentication.getPrincipal());
        return user;
    }

    public boolean hasFunction(String functionCode) {
        return getCurrUser().hasFunctionCode(functionCode);
    }

}

6.看看自定义的密码加密和配对的类,encode方法应该是加密传进来的密码用的,而matches是用 UserDetailsService接口的loadUserByUsername 方法中查出来的密码和登录页面输入进来的密码进行匹配。网上可以搜到其他的框架的加密方式,例如根据我在开头说的那个报错去查找相关信息。

package com.didispace.security;

import org.springframework.security.crypto.password.PasswordEncoder;

public class MyPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {
        System.out.println("chars encode: " + charSequence.toString());
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        System.out.println("chars : " + charSequence.toString());
        System.out.println("s : " + s);
        return s.equals(charSequence.toString());
    }
}

 

7.接下来是几个用户角色的表和实体类,有过经验的都应该知道怎么设计,我的如下:

User实体类:

package com.didispace.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "USERS")
public class User {

    @Id
    @Column(name = "USER_ID")
    private String userId;

    @Column(name = "USER_NAME")
    private String userName;

    @Column(name = "PASSWORD")
    private String passWord;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }
}

Role类:

package com.didispace.domain;


import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "SYS_ROLE")
public class Role {

    @Id
    @Column(name = "ROLE_ID")
    private String roleId;

    @Column(name = "ROLE_NAME")
    private String roleName;

    @Column(name = "ORDER")
    private Integer order;

    public String getRoleId() {
        return roleId;
    }

    public void setRoleId(String roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public Integer getOrder() {
        return order;
    }

    public void setOrder(Integer order) {
        this.order = order;
    }
}

UserRole类:

package com.didispace.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "USER_ROLE")
public class UserRole {

    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "USER_ID")
    private String userId;

    @Column(name = "ROLE_ID")
    private String roleId;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getRoleId() {
        return roleId;
    }

    public void setRoleId(String roleId) {
        this.roleId = roleId;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

Func(权限)类:

package com.didispace.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "SYS_FUNC")
public class Func {

    @Id
    @Column(name = "FUNCTION_ID")
    private String functionId;

    @Column(name = "FUNCTION_NAME")
    private String functionName;

    @Column(name = "ORDER")
    private Integer order;

    @Column(name = "FUNCTION_URL")
    private String functionUrl;

    public String getFunctionId() {
        return functionId;
    }

    public void setFunctionId(String functionId) {
        this.functionId = functionId;
    }

    public String getFunctionName() {
        return functionName;
    }

    public void setFunctionName(String functionName) {
        this.functionName = functionName;
    }

    public Integer getOrder() {
        return order;
    }

    public void setOrder(Integer order) {
        this.order = order;
    }

    public String getFunctionUrl() {
        return functionUrl;
    }

    public void setFunctionUrl(String functionUrl) {
        this.functionUrl = functionUrl;
    }
}

RoleFun类:

package com.didispace.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "ROLE_FUNC")
public class RoleFunc {

    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "ROLE_ID")
    private String roleId;

    @Column(name = "FUNC_ID")
    private String funcId;

    public String getRoleId() {
        return roleId;
    }

    public void setRoleId(String roleId) {
        this.roleId = roleId;
    }

    public String getFuncId() {
        return funcId;
    }

    public void setFuncId(String funcId) {
        this.funcId = funcId;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

8.下面是权限的Service类,用以实现上述代码中的某些方法:

FuncService:

package com.didispace.service;

import com.didispace.domain.Func;
import com.didispace.domain.RoleFunc;
import com.didispace.domain.User;
import com.didispace.domain.UserRole;
import com.didispace.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class FuncService {

    @Autowired
    private FuncRepository funcRepository;
    @Autowired
    private RoleFuncRepository roleFuncRepository;
    @Autowired
    private UserRoleRepository userRoleRepository;
    @Autowired
    private UserRepository userRepository;

    public Func getFunByUrl(String url) {
        Func func = funcRepository.findByFunctionUrl(url);
        return func;
    }

    public List<String> findFunctionCodesByRoleIds(String userName) {
        User user = userRepository.findByUserName(userName);
        List<UserRole> userRoles = userRoleRepository.findAllByUserId(user.getUserId());
        List<String> roleIds = new ArrayList<>();
        for (int i=0;i<userRoles.size();i++) {
            roleIds.add(userRoles.get(i).getRoleId());
        }
        List<RoleFunc> roleFuncs = roleFuncRepository.findDistinctByRoleId(roleIds);
        List<String> codes = new ArrayList<>();
        for (RoleFunc roleFunc:roleFuncs) {
            codes.add(roleFunc.getFuncId());
        }
        return codes;
    }

}

9.然后接下来就看页面啦,先是Controller:

package com.didispace.web;

import com.didispace.util.UserUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;

@Controller
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "index";
    }

    @RequestMapping("/hello")
    public String hello(HttpServletRequest request) {
        request.setAttribute("remoteUser", UserUtil.getCurrUser());
        return "hello";
    }

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login() {
        return "login";
    }

    @RequestMapping("/helloOne")
    public String helloOne() {
        return "hello12";
    }

}

还有个用来试试Ajax的Controller:

package com.didispace.web;

import com.didispace.model.AjaxResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestAjaxController {

    @RequestMapping("/getAjaxMessage")
    public AjaxResult getAjaxMessage() {
        return new AjaxResult("測試","0000");
    }

}
package com.didispace.model;

public class AjaxResult {

    private String message;

    private String code;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public AjaxResult(String message, String code) {
        this.message = message;
        this.code = code;
    }
}

 

10.接下来就是Html页面,用的 thymeleaf 模板引擎:

打招呼的index界面:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Spring Security入门</title>
</head>
<body>
<h1>欢迎使用Spring Security!</h1>

<p>点击 <a th:href="@{/hello}">这里</a> 打个招呼吧</p>
</body>
</html>

先来看login页面:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
    用户名或密码错
</div>
<div th:if="${param.logout}">
    您已注销成功
</div>
<form th:action="@{/login}" method="post">
    <div><label> 用户名 : <input type="text" name="username"/> </label></div>
    <div><label> 密 码 : <input type="password" name="password"/> </label></div>
    <div><input type="submit" value="登录"/></div>
</form>
</body>
</html>

然后是登录成功后重定向的hello页面:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Hello World!</title>
</head>
<body>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
    <input type="submit" value="注销"/>
</form>
</body>
</html>

接下来说一下Ajax的页面,在config类配置的时候,如果没有配置 csrf().disable()的话,security的csrf防护是开启的,所以ajax发不过去,我查了下大家可以看看这个链接,我下面的html就是用这个方法:

https://blog.csdn.net/sinat_28454173/article/details/52251004

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
    <title>Title</title>
    <meta name="_csrf" th:content="${_csrf.token}"/>
    <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>
<!--<script type="text/javascript" th:src="@{/js/jquery-1.10.2.js}"></script>-->
<script type="text/javascript" th:src="@{/js/min/jquery-1.12.4.min.js}"></script>
<!--<script type="text/javascript" src="../../js/index.js}"></script>-->
<script type="text/javascript">var context_path = '..';</script>
<script type="text/javascript">

    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");

    function getAjaxMessage() {
        alert("123");
        $.ajax({
            url: "getAjaxMessage",
            type:"post",
            datatype: "json",
            beforeSend : function(xhr) {
                xhr.setRequestHeader(header, token);
            },
            success: function(result) {
                alert(result.message);
            },
            error: function(XMLHttpRequest, textStatus, errorThrown) {
                alert(textStatus + " " + XMLHttpRequest.readyState + " "
                    + XMLHttpRequest.status + " " + errorThrown);
            }
        });
    }

</script>
<body>

<h1> hello welcome !! </h1>
<span id="ajaxMessage"></span>

<input th:if="${session.userUtil.hasFunction('FUNC_HELLO')}" type="button" onclick="getAjaxMessage()" />

</body>
</html>

事实上我也试过这个链接

http://www.cnblogs.com/yjmyzz/p/customize-CsrfFilter-to-ignore-certain-post-http-request.html

中说到的自定义一个 RequestMatcher,具体是这样:

http.csrf().disable().requestMatcher(new CsrfSecurityRequestMatcher());
package com.didispace.security;

import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class CsrfSecurityRequestMatcher implements RequestMatcher {

    private List<String> execludeUrls = new ArrayList<>();

    private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");

    @Override
    public boolean matches(HttpServletRequest httpServletRequest) {
        System.out.println(" come to match !!");
        if (!execludeUrls.contains("/helloOne")) {
            execludeUrls.add("/helloOne");
            execludeUrls.add("/getAjaxMessage");
        }
        if (execludeUrls != null && execludeUrls.size() > 0) {
            String servletPath = httpServletRequest.getServletPath();
            for (String url : execludeUrls) {
                if (servletPath.contains(url)) {
                    return false;
                }
            }
        }
        return !allowedMethods.matcher(httpServletRequest.getMethod()).matches();
    }

    public List<String> getExecludeUrls() {
        return execludeUrls;
    }

    public void setExecludeUrls(List<String> execludeUrls) {
        this.execludeUrls = execludeUrls;
    }

}

return false 就是允许通过了。但是配了之后发现请求绕过了 security不用登录验证都能访问,我就觉得应该缺了某些东西,可能在security里面match之前登录验证的逻辑是存在的,我自定义这个类后应该导致验证逻辑缺少了,具体还要研究一下怎么用才行。

 

11.最后上一下appsetting和pom文件:

server.port=8080

server.servlet.context-path=/test

spring.resources.static-locations=classpath:/static/
spring.mvc.static-path-pattern=/**
<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>

	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<fork>true</fork>
				</configuration>
			</plugin>
		</plugins>
	</build>

好了,简单的security就到此,大家有兴趣可以看看spring的官方文档研究一下。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值