本人最近钻研SpringMVC,都是学习感悟与个人看法,接受任何提问与建议,希望大家提出自己独特的见解,谢谢。
也可以进入我的b站主页,观看在线教程,视频中写出了当前文章中未出现的技术,而且在拦截器上也做出了许多改动,使用了两个拦截器来进行认证与权限;
1-总体
2-首先是我们的pom.xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jstl-impl</artifactId>
<version>1.2</version>
<!-- 去除依赖 -->
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-websocket -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<!--redis -->
<!--spring-data-redis版本不能太高害怕有冲突 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.5.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
</dependencies>
3-Configuration配置类
1.ServletConfig, 我们的一些拦截器,或者处理器,视图解析器,都可以在此配置类进行配置,它是实现了WebMvcConfigurer的方法;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.cjj.controller")
public class ServletConfig implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver viewResolver=new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".html");
viewResolver.setExposeContextBeansAsAttributes(true);
return viewResolver;
//InternalResourceViewResolver 是 Spring MVC 中最常用的视图解析器
// 它用于解析 JS P或 HTML 等资源文件。该解析器会将逻辑视图名称加上前缀和后缀
// 例如将逻辑视图名称 “hello” 解析为 “/WEB-INF/views/hello.html”。
}
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Bean
public MyInterceptor getMyInterceptor(){
return new MyInterceptor();
}
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getMyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login2")
.excludePathPatterns("/")
.excludePathPatterns("/testredis")
.excludePathPatterns("/image/**")
.excludePathPatterns("/js/**")
.excludePathPatterns("/css/**");
}
}
2.RootConfig,用于配置我的数据库,完全实现用注解,xml文件不需要使用;
@Configuration
// Reposity @service @Bean
@MapperScan(basePackages="com.cjj.Dao")
@ComponentScan(basePackages = "com.cjj" ,excludeFilters =
{@ComponentScan.Filter(type = FilterType.ANNOTATION,value ={EnableWebMvc.class, Controller.class})})
//是除了web层的所有的Bean
@PropertySource({"classpath:jdbc.properties"})
public class RootConfig {
@Value("${driverClass}")
private String driver;
@Value("${url}")
private String url;
@Value("${root}")
private String root;
@Value("${password}")
private String password;
@Bean(name="datasource")
public DataSource getDataSource(){
ComboPooledDataSource dataSource=new ComboPooledDataSource();
try {
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(root);
dataSource.setPassword(password);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
}
@Bean(name = "jdbctemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);
return jdbcTemplate;
}
//mybatis的配置
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException {
System.out.println("SqlSessionFactoryBean---------------------------");
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();//mybatis-plus插件类
sqlSessionFactoryBean.setDataSource(getDataSource());//数据源
sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources("classpath:mappers/*.xml"));
return sqlSessionFactoryBean;
}
}
让我们可以不需要配置xml文件,就能访问到mappers下的所有映射文件,jdbc.properties我就不献上了,大家根据自己的数据库配置来;
3. MvcApplication,实现我们的AbstractAnnotationConfigDispatcherServletInitializer,就不用多说了,用于配置我们的容器,很关键的一个容器类;
public class MvcApplication extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
//返回的带有@Configuration注解的类用来配置ContextLoaderListener;
System.out.println("RootConfig-----------");
return new Class[]{RootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
//将一个或多个路径映射到DispatcherServlet上;
System.out.println("ServletConfig-----------");
return new Class[]{ServletConfig.class};
}
@Override
protected String[] getServletMappings() {
// /*拦截所有http请求,包括.jsp,都作为控制器类的请求路径处理;
System.out.println("getServletMappings-----------");
return new String[]{"/"};
}
}
4- 创建Model实体类
配置全都部署好了,接下来开始我们的实体类
User
@Component
public class User {
private int id;
private String username;
private String password;
private String token;
private int rid;
private List<privilege> pprivilegerList;
public User() {
}
public User(String username, String password, String token) {
this.username = username;
this.password = password;
this.token = token;
}
public User(int id, String username, String password, String token, int rid) {
this.id = id;
this.username = username;
this.password = password;
this.token = token;
this.rid = rid;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getRid() {
return rid;
}
public void setRid(int rid) {
this.rid = rid;
}
public List<privilege> getPprivilegerList() {
return pprivilegerList;
}
public void setPprivilegerList(List<privilege> pprivilegerList) {
this.pprivilegerList = pprivilegerList;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", token='" + token + '\'' +
", rid=" + rid +
'}';
}
}
Privilege,我的权限是用来测试的,我也不清楚,我的用法对不对,希望大家指点;
@Component
public class privilege {
private int id;
private String demoname;
private int pid;
public privilege() {
}
public privilege(int id, String demoname, int pid) {
this.id = id;
this.demoname = demoname;
this.pid = pid;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDemoname() {
return demoname;
}
public void setDemoname(String demoname) {
this.demoname = demoname;
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
}
5- 接口,实现映射
@Repository
@Mapper
public interface UserDao {
public User login(User user);
public List<privilege> getprivilege(User user);
}
<mapper namespace="com.cjj.Dao.UserDao">
<select id="login" parameterType="com.cjj.Model.User" resultType="com.cjj.Model.User">
select id,username,password,token,rid FROM `user` where `user`.username=#{username} and `user`.`password`=#{password}
</select>
<resultMap id="wei" type="com.cjj.Model.privilege" >
<id column="id" property="id"/>
<result column="demoname" property="demoname"/>
<result column="rid" property="rid"/>
</resultMap>
<select id="getprivilege" parameterType="com.cjj.Model.User" resultMap="wei">
SELECT a.id ,b.demoname ,b.pid FROM user
INNER JOIN role ON role.id = user.rid
INNER JOIN privilege a ON a.id = role.id
INNER JOIN privilege b on a.id = b.pid
WHERE user.rid= #{rid}
</select>
</mapper>
6-创建业务方法,service
public interface UserServiceIdao {
public User login(User user);
public List<privilege> getprivilege(User user);
}
实现接口
@Service
public class UserService implements UserServiceIdao {
@Autowired
private UserDao userDao;
@Override
public User login(User user) {
return userDao.login(user);
}
@Override
public List<privilege> getprivilege(User user) {
return userDao.getprivilege(user);
}
}
7-JwtBuilder
在咱们进入控制层之前,咱们先来配置好jwt类的生成与解析
public class JWTuuid {
private static final long time =60*60*24*1000;
private static final String signature="weifeng00000x";//秘钥
public static final String createjwt(String name, User user)
{
JwtBuilder jwtBuilder= Jwts.builder();
String jwttoken=jwtBuilder
//header -- 设置头
.setHeaderParam("typ","JWT")
.setHeaderParam("alg","HS256")//加密算法
//payload----载荷
.claim("username",name)
.claim("stu", user)
//主题
.setSubject("weifeng-test")
//有效时间
.setExpiration(new Date(System.currentTimeMillis()+time))//从当前开始,24小时
//jwt id
.setId(UUID.randomUUID().toString())
//签名 signature
.signWith(SignatureAlgorithm.HS256,signature)
.compact();
return jwttoken;
}
// 解析 JWT
public static Claims testJWT(String jwt) {
try{
Claims claims = Jwts.parser()
.setSigningKey(signature) //放入秘钥
.parseClaimsJws(jwt) //放入需要解析的串
.getBody();
return claims;
}catch (Exception e){
e.printStackTrace();
// 如果对于秘钥解析错误那么放回null
return null;
}
}
public static boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
8-Controller控制层
终于来到了咱们的控制层
@RestController
public class UserController {
@Autowired
UserService userService;
@CrossOrigin(origins = "http://localhost:63342")
@PostMapping("/login2")
@ResponseBody
public Object login2(@RequestBody JSONObject json, HttpServletRequest req, HttpServletResponse response)
{
String username=json.getString("username");
String password=json.getString("password");
User user =new User(username,password,"");
System.out.println("username "+username+"-----pwd "+password);
User login = userService.login(user);
System.out.println("login的值为---------"+login);
if(login!=null)
{
req.getSession().setAttribute("user",login);
String createjwt = JWTuuid.createjwt(username, user);
user.setToken(createjwt);
System.out.println("jwt码--"+createjwt);
// 将token写入响应头中
response.addHeader("Authorization", "Bearer " + createjwt);
response.setContentType("application/json;charset=utf-8");
return createjwt;
}
else {
String error="用户名或密码错误";
// 设置HTTP状态码为401 Unauthorized
response.setStatus(HttpStatus.UNAUTHORIZED.value(),"用户名或密码错误");
//response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json;charset=utf-8");
// 返回
return error;
}
}
@RequestMapping(value = "/aa",method = RequestMethod.GET)
public ModelAndView getaa(HttpServletRequest req)
{
System.out.println("进来这个方法 getaa()");
ModelAndView mv=new ModelAndView();
mv.setViewName("aa");
return mv;
}
@GetMapping("/demo")
@ResponseBody
public Object getstu(@RequestParam("demo") int demo,HttpServletRequest req){
System.out.println("进来这个方法 getstu()");
System.out.println("demo : "+demo);
// System.out.println("token: "+token);
// Claims claims=JWTuuid.testJWT(token);
// if(claims==null){
// System.out.println("jwt码有误,勿扰");
// return "jwt码有误,勿扰";
// }
List<privilege> user = userService.getprivilege((User) req.getSession().getAttribute("user"));
for (privilege s : user) {
System.out.println(s.getDemoname());
}
// String obj=claims.getSubject();
// System.out.println("提交主题:"+obj);
return user;
}
}
其实我的登录是有些问题的,我返回的直接是个字符串,照理说应该是一个result集合,然后给状态或是其他什么的,将就看看吧,哈哈;
9-HandlerInterceptor拦截器
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("===============请求处理前(1)===================");
String path=request.getRequestURL().toString();
if(path.indexOf("/login2")>=0){
System.out.println("确实是login方法------");
return true;
}
System.out.println("--------------------------------------"+path);
Object obj=request.getSession().getAttribute("user");
System.out.println(obj+"---------------------MyInterceptor");
if(obj!=null)
return true;
// 获取Authorization请求头中的token
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
System.out.println("截取的token");
// 验证token
if (JWTuuid.validateToken(token)) {
return true; // 验证通过,继续执行后续操作
}
}
//request.setAttribute("msg","还没有登陆,请先登录");
request.getRequestDispatcher("index.html").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
System.out.println("===============请求处理后,生成视图之前(2)===================");
//assert modelAndView != null;
// System.out.println("modelviewname============="+modelAndView.getViewName());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
System.out.println("===============结束时调用(3)===================");
}
}
我觉得我的拦截器,写的有问题,虽然能成功使用,但还是希望大家提出各自独特的见解;
10-前端界面
index.html
<html>
<head>
<meta charset="UTF-8">
<title>$Title$</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
用户:<input id="username" type="text" name="username" placeholder="请输入用户">
密码:<input id="password" type="password" name="password" placeholder="请输入密码">
<button id="login" value="登录"/>
登录
<button type="reset" value="重置"/>
重置
<script>
$("#login").click(function (){
let username = document.getElementById("username").value;
let password = document.getElementById("password").value;
let user={};
user.username=username;
user.password=password;
console.log(user);
$.ajax({
url: 'http://localhost:9090/login2', // 请求的API接口地址
//http://iwenwiki.com:8088/api/FingerUnion/list.php
type: 'POST',
// headers:{
// token:"wawa"
// },
dataType: 'text',
contentType:"application/json",
data:JSON.stringify(user) ,
success: function (e) {
alert("key为:"+e);
console.log("key为:"+e);
localStorage.setItem('token', e);
sessionStorage.setItem('token',e);
window.location.href="http://localhost:9090/aa";
// TODO: 处理接收到的数据
},
error: function (e) {
alert("账号或密码错误!!");
}
});
});
// let p=new Promise((resolve, reject) =>{
</script>
</body>
</html>
aa.html 拿到当前用户的操作权限的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<p id="pp">你好你好你好你好喝牛奶</p>
操作功能:<button type="button" id="caoz" value="请操作"/>
</body>
</html>
<script>
$("#caoz").click(function (){
$.ajax({
url: 'http://localhost:9090/demo', // 请求的API接口地址
type:"get",
dataType: 'json',
contentType:"application/json",
data:{
demo:20040309,
},
headers:{
"Authorization":localStorage.getItem('token'),
},
success:function (e){
alert("success回调执行了")
for(var i = 0; i < e.length; i++) {
var name = e[i].demoname;
console.log("权限有", name);
}
},
error:function (e){
alert("error回调执行了")
},
});
})
</script>
到此,我个人学习的收获就完了,希望能看到这里的朋友,学习永无瓶颈,天天开心,一直保持着一颗追求新技术的心。